An open API service indexing awesome lists of open source software.

https://github.com/compulim/bookstore

A data storage pattern for Azure Blob Storage
https://github.com/compulim/bookstore

Last synced: 4 days ago
JSON representation

A data storage pattern for Azure Blob Storage

Awesome Lists containing this project

README

        

```
_ _ _
| |__ ___ ___ | | _____| |_ ___ _ __ ___
| '_ \ / _ \ / _ \| |/ / __| __/ _ \| '__/ _ \
| |_) | (_) | (_) | <\__ \ || (_) | | | __/
|_.__/ \___/ \___/|_|\_\___/\__\___/|_| \___|

```

A small data framework for collaborative list editing using Azure Blob Storage and Redis.

npm version
Build Status
Coverage Status

# 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`.

```js
const { createBlobService } = require('azure-storage');
const { createClient } = require('redis');
const updateIn = require('simple-update-in');
const createBook, { createPubSubUsingRedis, createStorageUsingAzureStorage } = require('bookstore');

const blobService = createBlobService(
process.env.AZURE_STORAGE_ACCOUNT,
process.env.AZURE_STORAGE_ACCESS_KEY
);

const publishRedis = createClient();
const subscribeRedis = publishRedis.duplicate();

const book = createBook(
({ x, y }) => ({ sum: x + y }),
{
...createPubSubUsingRedis(publishRedis, subscribeRedis, 'blob-container-name'),
...createStorageUsingAzureStorage(blobService, 'blob-container-name')
}
);

await book.create('page-0', { x: 1, y: 2 });

// Listing summary of all pages
// { 'page-0': {
// summary: { sum: 3 }
// } }
await book.list();

// Getting the content of the page
// { x: 1, y: 2 }
await book.get('page-0');

// Update a page
// We use `simple-update-in` and updater function for handling concurrency
await book.update('page-0', content => updateIn(content, ['x'], () => 3));

// "change" event emitted when update broadcast on Redis
// Only summaries are sent over Redis
book.subscribe(({ id, summary }) => {
console.log(id); // 'page-0'
console.log(summary); // { sum: 5 }
});

// Listing summary of all pages again, with new changes
// { 'page-0': {
// summary: { sum: 5 }
// } }
await book.list();

// Delete a page
await book.del('page-0');
```

## 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 summary
- `del(id)`: Delete a blob
- `get(id)`: Read a blob content
- `list()`: List all blob summaries, without reading the actual content
- `update(id, updater)`: Update an existing blob via an updater function, using lock to prevent dirty read
- `updater: ({ content, summary }) => ({ content, summary })`
- Pub-sub
- `publish(content)`: Publish to a predefined topic
- `subscribe(callback: content => void): () => void`: Subscribe to a predefined topic via callback, will return a function for unsubscribe
- `callback` will be called if content is updated (via `Object.is`) and summary has kept the same

# Contributions

Like us? [Star](https://github.com/compulim/bookstore/stargazers) us.

Want to make it better? [File](https://github.com/compulim/bookstore/issues) us an issue.

Don't like something you see? [Submit](https://github.com/compulim/bookstore/pulls) a pull request.