RabbitMQ Setup with Dead Letter Handling
To handle timed‑order expiry, a RabbitMQ infrastructure consisting of a direct exchange, a durable queue, and a dedicated dead‑letter exchange (DLX) is defined. The queue is declared with arguments that route expired (unconsumed) messages to the DLX after a configured TTL. Below is the Spring configuration:
@Configuration
public class RabbitInfraConfig {
@Bean
public DirectExchange purchaseExchange() {
return new DirectExchange("purchase.exchange");
}
@Bean
public Queue purchaseQueue() {
return QueueBuilder.durable("queue.purchase")
.withArgument("x-dead-letter-exchange", "purchase.dlx.exchange")
.withArgument("x-dead-letter-routing-key", "purchase.dlx.route")
.build();
}
@Bean
public DirectExchange purchaseDlxExchange() {
return new DirectExchange("purchase.dlx.exchange");
}
@Bean
public Queue purchaseDlxQueue() {
return new Queue("queue.purchase-dlx");
}
@Bean
public Binding bindPurchase() {
return BindingBuilder.bind(purchaseQueue())
.to(purchaseExchange())
.with("purchase.key");
}
@Bean
public Binding bindPurchaseDlx() {
return BindingBuilder.bind(purchaseDlxQueue())
.to(purchaseDlxExchange())
.with("purchase.dlx.route");
}
}
Here purchase.exchange is the main exchange and queue.purchase is the primary queue. The dead‑letter exchange purchase.dlx.exchange receives messages that exceed their time‑to‑live (e.g., 30 minutes) via the routing key purchase.dlx.route.
Redis Flash Sale with Redisson Lock and Lua Script
A flash sale (seckill) implementation uses Redisson for distributed locking and an atomic Lua script executed inside Redis. The script checks available stock and decrements it if sufficient, returning 1 for success and 0 for failure.
public class FlashSaleProcessor {
private final RedissonClient redissonClient;
private final String reductionScript;
public FlashSaleProcessor(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
this.reductionScript =
"local available = redis.call('get', KEYS[1])\n" +
"if available and tonumber(available) >= tonumber(ARGV[1]) then\n" +
" redis.call('decrby', KEYS[1], ARGV[1])\n" +
" return 1\n" +
"else\n" +
" return 0\n" +
"end";
}
public boolean executeFlashSale(String itemCode, int amount) {
RLock lock = redissonClient.getLock(itemCode);
try {
lock.lock();
RScript script = redissonClient.getScript();
List<Object> result = script.eval(
RScript.Mode.READ_WRITE,
reductionScript,
RScript.ReturnType.INTEGER,
Collections.singletonList(itemCode),
String.valueOf(amount)
);
return result != null && !result.isEmpty() && (int) result.get(0) == 1;
} finally {
lock.unlock();
}
}
}
The constructor loads the Lua script once. The executeFlashSale method acquires a distributed lock on the item code, runs the script inside Redis, and interprets the result. Lock release is ensured in the finally block.
Elasticsearch for System Log Retrieval
System logs are indexed into Elasticsearch, enabling fast full‑text search and aggregation. Log shippers (like Filebeat) push logs into a dedicated index, and queries can filter by timestamp, severity, or custom fields. Kibana dashboards visualize log activity but the core retrieval relies on Elasticsearch’s search API.
Redis‑Powered Features Using Multiple Data Types
Like/Unlike with Sets
A Redis Set stores user IDs who liked a post. SADD post:{id}:likes userId records a like; SREM removes it. SCARD returns the total likes. To check if a user liked, SISMEMBER is used.
Nearby Shop Discovery with GEO
Shop locations are stored using the GEO data type. GEOADD shops:locations lon lat shopId adds a shop. Queries like GEORADIUS shops:locations lon lat radius km return sorted results with optional WITHDIST.
Daily Check‑in with Bitmap
A Bitmap tracks user check‑ins per month (e.g., key checkin:2024:04:uid). SETBIT key day-of-month 1 marks attendance. BITCOUNT key counts total days. Bitfields can compute consecutive streaks.
Unique User Statistics with HyperLogLog
HyperLogLog approximates unique visitors with minimal memory. PFADD page:uv userId adds a user; PFCOUNT page:uv returns the estimated unique count. Multiple HLL keys can be merged with PFMERGE for aggregated stats.
Resumable File Upload with Chunk Tracking
A file is split into chunks, and each uploaded chunk is recorded in a database table. Before uploading, the client queries the highest sequence number already stored to continue from the next chunk. The implementation uses SQL to track progress:
SELECT sequence_no
FROM chunk_uploads
WHERE file_id = 'file123'
ORDER BY sequence_no DESC LIMIT 1;
If the upload fails, the client can resume from sequence_no+1. Once the entire file is reconsturcted, all entries for that file are deleted:
DELETE FROM chunk_uploads WHERE file_id = 'file123';
Data loss considerations: Relying solely on a database for chunk tracking introduces a risk if a chunk is successfully written to storage but the corresponding database record fails to insert (e.g., due to a transaction rollback). To mitigate this, the upload process should be designed with idempotent chunk storage and a two‑phase commit pattern where the chunk record is only inserted after the file store persists the data, or by using atomic metadata updates on the storage system itself.