Sikau

Project: Ready for millions · Day 3 · about 40 min

A cached, versioned data layer

Read-through and write-through caching over DynamoDB, with optimistic locking.

Fast reads, no lost updates

Reads dominate, so put your LRU cache (System design) in front of the DynamoDB table (AWS). Two dangers: serving stale data after a write, and two writers overwriting each other. Write-through updates the cache on every write; optimistic locking gives each item a version, and a write that expected an older version is rejected.

The repository

function createCachedRepo({ table, cache }) {
  let dbReads = 0;
  let dbWrites = 0;
  const read = (id) => {
    dbReads++;
    return table.get(id);
  };
  const write = (item) => {
    dbWrites++;
    table.put(item);
    cache.set(item.id, item); // write-through: the cache never holds an old version
    return item;
  };
  return {
    create(item) {
      return write({ ...item, version: 1 });
    },
    get(id) {
      const cached = cache.get(id);
      if (cached !== undefined) return cached;
      const item = read(id) ?? null;
      if (item) cache.set(id, item);
      return item;
    },
    update(id, changes, expectedVersion) {
      const current = read(id);
      if (!current) throw new Error("Not found");
      if (expectedVersion !== undefined && current.version !== expectedVersion) throw new Error("Version conflict"); // optimistic locking
      return write({ ...current, ...changes, id, version: current.version + 1 });
    },
    stats: () => ({ dbReads, dbWrites, ...cache.stats() }),
  };
}

The rest of this lesson

  • Read-through and write-throughexplain
  • Optimistic lockingexplain
  • Two editorsorder
  • Term to behaviourmatch
  • Build the repositorywrite code
  • Badge quizquiz
  • What you builtrecap

Continue this lesson free — run real code in your browser

Continue with Google

See all 4 lessons of Project: Ready for millions