_ _ _
| |__ ___ ___ | | _____| |_ ___ _ __ ___
| '_ \ / _ \ / _ \| |/ / __| __/ _ \| '__/ _ \
| |_) | (_) | (_) | <\__ \ || (_) | | | __/
|_.__/ \___/ \___/|_|\_\___/\__\___/|_| \___|
A small data framework for collaborative list editing using Azure Blob Storage and Redis.
What is different than traditional CRUD + cache?
- Content and summary
- Summary is computed from content, via a summarizer function
- When content is updated, only summary is broadcasted via Redis to other nodes
- List will fetch all summaries, without content
- List is cheap,
O(1)
- Update is done thru lock-update-unlock pattern with updater function
- Updater function can be programmed as optimistic or pessimistic concurrency
- We believe this model makes concurrency issues a little bit easier to handle
bookstore
is designed to be exposed over Web Socket and Server-Sent Events.
How to use
For production build, npm install bookstore
. For development build, npm install bookstore@master
.
For peer dependencies, you will also need npm install azure-storage@2 redis
.
const createBlobService = ;const createClient = ;const updateIn = ;const createBook createPubSubUsingRedis createStorageUsingAzureStorage = ; const blobService = ; const publishRedis = ;const subscribeRedis = publishRedis; const book = ; await book; // Listing summary of all pages// { 'page-0': {// summary: { sum: 3 }// } }await book; // Getting the content of the page// { x: 1, y: 2 }await book; // Update a page// We use `simple-update-in` and updater function for handling concurrencyawait book; // "change" event emitted when update broadcast on Redis// Only summaries are sent over Redisbook; // Listing summary of all pages again, with new changes// { 'page-0': {// summary: { sum: 5 }// } }await book; // Delete a pageawait book;
Peer requirements
Instead of using Blob via Azure Storage and Pub-sub via Redis, you can also use other services as long as they met the requirements:
- Storage
create(id, content, summary)
: Create a new blob with content and summarydel(id)
: Delete a blobget(id)
: Read a blob contentlist()
: List all blob summaries, without reading the actual contentupdate(id, updater)
: Update an existing blob via an updater function, using lock to prevent dirty readupdater: ({ content, summary }) => ({ content, summary })
- Pub-sub
publish(content)
: Publish to a predefined topicsubscribe(callback: content => void): () => void
: Subscribe to a predefined topic via callback, will return a function for unsubscribecallback
will be called if content is updated (viaObject.is
) and summary has kept the same
Contributions
Like us? Star us.
Want to make it better? File us an issue.
Don't like something you see? Submit a pull request.