Skip to content

Write-Around and Write-Behind Caching: Optimizing Write-Heavy Workloads

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Write. We cover key concepts, practical examples, and best practices to help you master this topic.

Write-around Caching bypasses the cache on write operations — data is written directly to the database, and the cache is populated only on subsequent reads (cache-aside). Write-behind caching (also called write-back) asynchronously batches writes to the database, offering lower write latency at the risk of data loss on cache failure.

flowchart TB
    subgraph Write-Around
        WA1[Client Write] --> WA2[Database]
        WA1 --> WA3[Cache - Bypassed]
        WA4[Client Read] --> WA5{Cache Hit?}
        WA5 -->|Miss| WA6[Read from DB, Update Cache]
        WA5 -->|Hit| WA7[Return Cached]
    end

    subgraph Write-Behind
        WB1[Client Write] --> WB2[Cache - Fast Ack]
        WB2 --> WB3[Async Queue]
        WB3 --> WB4[Batch Write to DB]
    end

What You'll Learn

  • Write-around: avoiding cache pollution from write-heavy workloads
  • Write-behind: buffering writes for throughput and batching
  • Trade-offs: consistency, durability, and latency
  • Combining strategies for different data access patterns

Why It Matters

Write-around prevents cache pollution from one-time writes that are never read again. Write-behind reduces write latency by 10-100x by batching and deferring database writes, but introduces a window of potential data loss.

Real-World Use

A logging service writes 10,000 log entries per second. Using write-behind, logs are buffered in Redis and flushed to PostgreSQL in batches of 500 every second. This reduces write latency from 20ms to 0.5ms and reduces database write load by 500x.

Write-Around Implementation

async function writeAround(table, data) {
  const result = await db.query(`INSERT INTO ${table} SET ?`, [data]);
  // Cache is NOT updated. Next read will populate it via cache-aside.
  return result.insertId;
}

async function readWithCacheAside(id) {
  const cacheKey = `post:${id}`;
  const cached = await cache.get(cacheKey);
  if (cached) return JSON.parse(cached);

  const [rows] = await db.query('SELECT * FROM posts WHERE id = ?', [id]);
  if (rows.length > 0) {
    await cache.setEx(cacheKey, 3600, JSON.stringify(rows[0]));
  }
  return rows[0];
}

Expected output:

Write: data goes to DB only, no cache overhead. Read on a new ID: fetches from DB, caches result. Subsequent reads: cache hit.

Write-Behind with In-Memory Buffer

class WriteBehindBuffer {
  constructor(flushInterval = 1000, batchSize = 100) {
    this.buffer = [];
    this.flushInterval = flushInterval;
    this.batchSize = batchSize;
    this.timer = setInterval(() => this.flush(), flushInterval);
  }

  async write(key, data) {
    this.buffer.push({ key, data, timestamp: Date.now() });
    if (this.buffer.length >= this.batchSize) {
      await this.flush();
    }
    return { queued: true };
  }

  async flush() {
    if (this.buffer.length === 0) return;
    const batch = this.buffer.splice(0, this.batchSize);
    try {
      const values = batch.map(item => [item.data]);
      await db.query('INSERT INTO logs (data) VALUES ?', [values]);
      console.log(`Flushed ${batch.length} entries`);
    } catch (err) {
      console.error('Flush failed, re-queuing batch:', err.message);
      this.buffer.unshift(...batch);
    }
  }

  shutdown() {
    clearInterval(this.timer);
    return this.flush();
  }
}

Expected output:

Writes return instantly (<1ms). Buffer flushes every 1s or 100 entries. On failure, batch is re-queued. On shutdown, remaining buffer is flushed.

Write-Behind with Redis Queue

const { Queue } = require('bullmq');

const writeQueue = new Queue('db-writes', {
  connection: { host: 'redis-queue', port: 6379 },
  defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 1000 } }
});

async function queueWrite(table, data) {
  await writeQueue.add('write', { table, data, timestamp: Date.now() });
  return { queued: true, id: data.id };
}

// Worker process
const { Worker } = require('bullmq');
const worker = new Worker('db-writes', async (job) => {
  const { table, data } = job.data;
  await db.query(`INSERT INTO ${table} SET ?`, [data]);
}, { concurrency: 5 });

Expected output:

Write returns immediately. Redis queue persists jobs. Worker processes 5 concurrent writes, retries on failure with exponential backoff.

Common Mistakes

  • Using write-around for data that is read immediately after write (e.g., user settings page) — the subsequent read will miss the cache.
  • Using write-behind for critical financial data where durability is more important than latency.
  • Not flushing the write-behind buffer before the application shuts down, causing data loss.
  • Setting the batch size too large, causing long DB queries that block other operations.
  • Not monitoring write-behind queue depth — a growing queue indicates the database cannot keep up.

Practice Questions

  1. How does write-around differ from write-through?
  2. When is write-around the best choice?
  3. What durability risks does write-behind introduce?
  4. How does batch size affect write-behind performance?
  5. What happens to buffered writes if the application crashes?

Challenge

Design a caching Strategy for a photo upload service. Photo metadata is read frequently; photo files are large and read rarely after upload. Choose write strategy for each. Implement write-behind with Redis for the metadata and write-around for the photo files.

FAQ

What is write-around caching?

Write-around bypasses the cache on writes, writing directly to the database. The cache is populated only on cache misses during reads (standard cache-aside).

What is write-behind caching?

Write-behind (write-back) acknowledges the write immediately to the client, queues it in a buffer, and asynchronously writes to the database in batches. Provides low latency but risks data loss.

Is write-behind safe for financial transactions?

Generally no. Financial transactions require immediate durability. Use write-through or no caching for critical financial data.

How do I prevent data loss in write-behind?

Use a persistent queue (Redis with AOF, RabbitMQ, Kafka) instead of an in-memory buffer. The queue survives crashes because data is on disk.

What is the ideal batch size for write-behind?

It depends on your database throughput. Start with 100-500 items or 1-second intervals. Monitor database CPU and adjust. Larger batches improve throughput but increase latency for individual items.

Mini Project

Build a logging service with two modes: write-around (logs go directly to DB) and write-behind (logs buffer in a Redis queue and flush to DB every 500ms). Compare write latency and throughput under a 1000 requests/second load. Measure the data loss window for write-behind.

What's Next

Continue with Write-Back Caching for a detailed comparison with write-behind and dirty-page management.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro