---
title: An In-Memory Data Store as the Swiss Army Knife of Travel Infrastructure
description: "How we use a single technology for caching, job queues, rate limiting, and session management — the versatility and the operational risks of a shared dependency."
canonical: https://nowah.xyz/blog/redis-swiss-army-knife
lastModified: "2026-08-07T03:54:03.264Z"
---

# An In-Memory Data Store as the Swiss Army Knife of Travel Infrastructure

How we use a single technology for caching, job queues, rate limiting, and session management — the versatility and the operational risks of a shared dependency.

One technology handles our caching, our job queues, our [rate limiting](/blog/rate-limiting-ai-agent-experience), and our session management. That's elegant simplicity or a terrifying single point of failure, depending on how you look at it.

We use an in-memory data store for all four use cases, and we're not unique in this. an in-memory data store's data structures are versatile enough to support wildly different access patterns. The question isn't whether an in-memory data store can do it (it can) but whether having one technology handle all of it is wise (it's complicated).

## Cache: reference data and sessions

![Illustration for this section](https://pics.nowah.xyz/website-media/infrastructure-047-img-1.webp)

an in-memory data store stores our airport and airline reference data. Thousands of airports, hundreds of airlines, queried dozens of times per second, changed once a month. The read-to-write ratio is astronomical. This is the ideal cache use case.

Cache reads are sub-millisecond. The same data from the database would be 2-5 milliseconds. At our query volume, the difference saves meaningful database load and improves response times for every flight search.

Session data also lives in an in-memory data store. Authentication tokens, conversation context for active sessions, and temporary state. Session data has a natural TTL (sessions expire after inactivity), which maps perfectly to an in-memory data store's built-in key expiration.

## Queues: seven named queues

Our background job infrastructure runs on an in-memory data store as well. Seven named queues for different job categories (booking operations, notifications, document generation, analytics, and several others) each backed by an in-memory data store data structures.

The queue library uses an in-memory data store lists and sorted sets to implement reliable job delivery with acknowledgment, retry, and dead letter functionality. Jobs are produced by the API server and consumed by dedicated worker processes.

an in-memory data store's persistence options provide durability guarantees for queued jobs. If an in-memory data store restarts, pending jobs survive. This is important for booking-related jobs where losing a job means a traveler doesn't get their confirmation email.

## Rate limiting: sliding window counters

![Supporting diagram](https://pics.nowah.xyz/website-media/infrastructure-047-img-2.webp)

Rate limit counters for every endpoint live in an in-memory data store. Each user's request count is tracked per endpoint per time window using sorted sets that implement sliding window counting.

The rate limiter writes to an in-memory data store on every API request (incrementing the counter) and reads on every request (checking the current count against the limit). This is high-frequency both reads and writes. The access pattern is: increment, check, and let the TTL handle cleanup.

an in-memory data store handles this well because the operations are atomic and fast. A single ZADD + ZRANGEBYSCORE per request completes in under a millisecond. The alternative (rate limiting in the database) would add 2-5 milliseconds of latency to every single API request.

## The single point of failure problem

Here's the honest assessment. If an in-memory data store goes down, four things break simultaneously.

Cache fails. Reference data lookups fall through to the database. The database handles the additional load, but response times increase. Tolerable.

Queues stop processing. No new jobs are delivered to workers. Booking confirmations, emails, [push notifications](/blog/launching-push-notifications-travelers-informed), and document generation all pause. The traveler already got their in-chat confirmation, but the follow-up email and [push notification](/blog/push-notification-travel-alerts) are delayed. Problematic.

Rate limiting stops. Without an in-memory data store, rate limit counters don't work. We could either deny all requests (too aggressive) or allow all requests (no protection). We choose to allow with elevated monitoring. Temporarily risky.

Sessions fail. Active session state is lost. Users might need to re-authenticate. Conversation context might need to be rebuilt. Annoying but recoverable.

The severity varies. Cache and rate limiting failures are tolerable for minutes. Queue and session failures are problematic within seconds.

## Mitigation strategies

We mitigate the single-point-of-failure risk several ways.

**an in-memory data store runs in a highly available configuration.** Replication provides failover if the primary instance fails. The failover is fast (seconds) and automatic. Most brief an in-memory data store outages are invisible to users.

**\[Health checks\]\(/blog/health\-checks\-liveness\-probes\-ai\-services\) verify an in\-memory data store connectivity\.** Our deep [health check](/blog/health-check-hierarchy) includes a an in-memory data store ping. If an in-memory data store is unreachable, the health check reports degraded, and the orchestrator can react (removing affected containers from the load balancer, for example).

**\[Graceful degradation\]\(/blog/graceful\-degradation\-slow\-ai\) for non\-critical uses\.** If an in-memory data store is unreachable, cache reads fall through to the database. Rate limiting degrades to a permissive mode. These fallbacks are designed and tested, not improvised.

**Queue persistence.** an in-memory data store's persistence ensures that queued jobs survive restarts. Between restarts, the queue library provides dead letter handling for jobs that fail due to temporary an in-memory data store unavailability.

## Should you split an in-memory data store?

The question we get asked: should you use separate an in-memory data store instances for each use case? Cache in one, queues in another, rate limits in a third?

For our scale, a single instance (with replication) is simpler to operate and sufficient in capacity. The combined workload (cache reads, queue operations, rate limit counters, session management) fits comfortably within an in-memory data store's throughput capabilities.

If we reached a scale where the workloads interfered with each other (a cache eviction spike causing queue latency, for example), we'd split. But splitting adds operational overhead: more instances to monitor, configure, and maintain. We'd rather optimize the single instance until the scaling benefit of splitting justifies the operational cost.

The metrics to watch: memory utilization (are cache and queue data competing for space?), operation latency (are slow operations on one data type affecting others?), and connection count (are all clients combined approaching the connection limit?).

## Managing an in-memory data store as critical infrastructure

If an in-memory data store is your multi-purpose infrastructure tool:

Monitor it like a database, not like a cache. Cache misses are normal. Queue processing lag is not. Rate limit counter failures are dangerous. Monitor each use case independently.

Set memory limits per use case if possible. Don't let cache data evict queue data. Configure eviction policies that prioritize the right data.

Test an in-memory data store failover regularly. Don't assume the failover works because you configured it. Actually test it. Kill the primary in staging and verify the failover is clean and fast.

Have fallback behavior for each use case. When an in-memory data store is unreachable, each consumer should degrade gracefully rather than crash. Cache consumers fall through to the database. Rate limit consumers allow with logging. Queue consumers wait and retry.

Document what breaks when an in-memory data store breaks. Your team should know the blast radius of a an in-memory data store outage without having to figure it out during the outage. Document it. Review it quarterly. Update it when you add new an in-memory data store use cases.

---

Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. [Plan your next trip](https://app.nowah.xyz).
