Hone logo
Hone
Problems

Building an Eventually Consistent Key-Value Store in JavaScript

This challenge asks you to implement a simplified, eventually consistent key-value store using JavaScript. Eventually consistent systems prioritize availability and partition tolerance over immediate consistency, meaning updates might not be immediately visible to all clients. This is a common pattern in distributed systems where network latency and failures are expected.

Problem Description

You are tasked with creating a JavaScript class called EventuallyConsistentStore. This store should allow clients to put (write) and get (read) key-value pairs. Due to the "eventual consistency" nature, reads might not immediately reflect the latest writes. The store will internally use a queue to simulate asynchronous updates and eventual propagation of changes.

Key Requirements:

  • put(key, value): Adds a key-value pair to the store. This operation should be asynchronous, simulating a delayed update. The put operation itself should return immediately.
  • get(key): Retrieves the value associated with a given key. This operation should return a Promise that resolves with the value if found, or undefined if the key doesn't exist. The Promise should resolve eventually, reflecting the state of the store after any pending updates have been applied.
  • Internal Queue: The store should maintain an internal queue of pending updates (key-value pairs). These updates should be processed asynchronously at regular intervals.
  • Asynchronous Processing: The processing of the update queue should be handled asynchronously (e.g., using setTimeout or setInterval).
  • Concurrency: The store should handle concurrent put and get operations gracefully.

Expected Behavior:

  • After a put operation, the value might not be immediately available when get is called.
  • The get operation should eventually return the latest value after the update has been processed.
  • If a key is not present, get should return undefined.
  • Multiple put operations should be processed in the order they were added to the queue.

Edge Cases to Consider:

  • What happens if get is called before any updates have been processed?
  • What happens if get is called while an update is being processed?
  • How should the store handle duplicate keys? (Last write wins is a reasonable assumption)
  • What happens if the queue is empty?

Examples

Example 1:

Input:
store.put('a', 1);
setTimeout(() => store.put('a', 2), 100); // Put a new value after 100ms
setTimeout(() => store.get('a'), 200); // Get the value after 200ms
Output: (Promise resolves to 2)
Explanation: The first put sets the value to 1. After 100ms, the value is updated to 2. After another 100ms (total 200ms), the get operation retrieves the latest value, which is 2.

Example 2:

Input:
store.put('b', 'hello');
setTimeout(() => store.get('b'), 50); // Get before processing
setTimeout(() => store.put('b', 'world'), 100); // Update after 100ms
Output: (Promise resolves to 'world')
Explanation: The initial get is called before the update is processed. The update is processed after 100ms, and the subsequent get retrieves the updated value 'world'.

Example 3:

Input:
store.get('c');
Output: (Promise resolves to undefined)
Explanation: The key 'c' has never been put into the store, so get should return undefined.

Constraints

  • The internal queue should have a maximum size of 100. If the queue is full when a new put is called, the new put should be ignored (no error should be thrown).
  • The asynchronous processing interval should be between 50ms and 200ms (inclusive).
  • All operations must be asynchronous.
  • The store should be implemented as a class.
  • The get method must return a Promise.

Notes

  • Consider using setTimeout or setInterval to simulate asynchronous processing.
  • Think about how to handle concurrent access to the store's internal data. You might need to use techniques like closures or locking to prevent race conditions.
  • The focus is on demonstrating the eventual consistency concept, not on building a production-ready distributed store. Error handling and more sophisticated concurrency control are not required.
  • The order of operations is important. Ensure that updates are processed in the order they are added to the queue.
  • You don't need to persist the data to disk; the in-memory store is sufficient.
Loading editor...
javascript