https://github.com/pocesar/js-async-atomic-store
An agnostic little store abstraction for reading, writing and appending on the same data from multiple sources in a locking manner, that allows concurrent/parallel (like from many async sources) write, append and read.
https://github.com/pocesar/js-async-atomic-store
agnostic array async async-sources asynchronous atomic javascript lock map object parallel race-conditions read storage typescript write
Last synced: 6 months ago
JSON representation
An agnostic little store abstraction for reading, writing and appending on the same data from multiple sources in a locking manner, that allows concurrent/parallel (like from many async sources) write, append and read.
- Host: GitHub
- URL: https://github.com/pocesar/js-async-atomic-store
- Owner: pocesar
- License: mit
- Created: 2020-01-26T04:54:31.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-02-08T21:49:00.000Z (over 5 years ago)
- Last Synced: 2024-10-18T06:28:13.908Z (12 months ago)
- Topics: agnostic, array, async, async-sources, asynchronous, atomic, javascript, lock, map, object, parallel, race-conditions, read, storage, typescript, write
- Language: TypeScript
- Homepage:
- Size: 108 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
[](https://www.npmjs.com/package/async-atomic-store)
[](https://www.npmjs.com/package/async-atomic-store)
[](https://www.npmjs.com/package/async-atomic-store)
[](https://travis-ci.org/pocesar/js-async-atomic-store)
[](https://coveralls.io/github/pocesar/js-async-atomic-store?branch=master)# async-atomic-store
An agnostic little store abstraction for reading, writing and appending on the same data from multiple sources in a locking manner, that allows concurrent/parallel (like from many async sources) write, append and read.
## API
The interface is simple, you provide your `read`, `write` and `data` methods, and it outputs an async-locking `write`, `read`, `append`, `lock` and `data` methods.
All those methods are async (return a promise), even if they have non-async data underneath, because of the async locking mechanism, which is similar to a `LOCK TABLE` and not `LOCK ROW`. This does not use loops or saturate the event loop.
If any method `throw`, the lock will be released and no changes are made.
```ts
const store = AsyncAtomicStore({
data: async () => { /* your underlying data */ },
read: async (key) => { /* query a database, read from your datasource */ },
write: async (key, value) => { /* writes in a atomic locking manner */ }
});// guaranteed to return that point-in-time value
const value = await store.read("id");// at this exact point, "id" will be "my value"
await store.write("id", "my value");// passes the current value to the callback, modify it then write at the same key.
// transformedValue = "my value + my value"
const transformedValue = await store.append("id", async (currentValue) => {
return `${currentValue} + ${currentValue}`;
});// locks everything, passes the current point-in-time datasource, and return an arbitrary value.
// This exists for everything that isn't covered by the other "keyed" methods, but you need to
// do some async work before returning a value
// value = "ok"
const value = await store.lock(async (datasource) => {
datasource[42] = "the answer";
return "ok";
});
```Provides the following type-safe adapters for your data:
### AsyncArray
```ts
import { AsyncArray } from 'async-atomic-store'const myArray = [];
const store = AsyncArray(myArray);
await store.write(0, "ok");
await store.lock(async (arr) => {
arr.push("item"); // there's no other way to manipulate an array other than using lock()
});myArray[0] === "ok";
myArray[1] === "item";
```### AsyncMap
```ts
import { AsyncMap } from 'async-atomic-store'const myMap = new Map();
const store = AsyncMap(myMap);
await store.write("str", "ing");myMap.get("str") === "ing";
```### AsyncObject
```ts
import { AsyncObject } from 'async-atomic-store'const myObj = Object.create(null);
const store = AsyncObject(myObj);
await store.lock(async (obj) => {
obj.goCrazy = {
with: {
it: true
}
}
});myObj.goCrazy.with.it === true;
```## Examples
Explicit implementations of different data sources
Using `Map`
```ts
import { AsyncAtomicStore } from 'async-atomic-store'const lockingMap = () => {
const map = new Map()return AsyncAtomicStore({
data: async () => map,
read: async (key) => map.get(key),
write: async (key, value) => map.set(key, value!)
})
}const store = lockingMap()
for (let i = 0; i < 10; i++) {
// don't await
setTimeout(() => {
store.append(1, (value) => {
return {
...value,
deep: {
object: {
1: (value ? value.deep.object[1] : 0) + i
// the first lock will initialize the value to 0
// all subsequent writes will use the current value,
}
}
}
})
}, Math.round(Math.random() * 2))
}assert(
(await store.data()).get(1)!.deep.object[1] === 45,
'Object value should always be 45'
)
```Using `Set`:
```ts
const setStore = () => {
const set = new Set()return AsyncAtomicStore({
read: async (index) => index ? [...set.values()][index] : undefined,
write: async (index, value) => {
const kvs = [...set.values()]
set.clear() // set needs to be recreated each time
kvs[index] = value!
kvs.forEach((v) => set.add(v))
await sleep(2)
},
data: async () => [...set.values()]
})
}const store = setStore()
await Promise.all([
store.write(0, '0'),
store.write(1, '5'),
store.write(1, '1'),
store.write(2, '7'),
store.write(2, '2'),
store.write(3, '3'),
])const data = await store.data()
assert(
data.join('') === '0123',
'set should be exactly 0123'
)
```Redundantly using `Atomics` and `SharedArrayBuffer`:
```ts
import { AsyncAtomicStore } from 'async-atomic-store'const uint8Array = () => {
const buffer = new SharedArrayBuffer(16)
const uint8 = new Uint8Array(buffer)return AsyncAtomicStore({
read: async (index) => Atomics.load(uint8, index),
write: async (index, value) => Atomics.store(uint8, index, value),
data: async () => uint8
})
}const store = uint8Array()
for (let i = 0; i < 16; i++) {
setTimeout(async () => {
store.append(1, (currentValue) => {
return currentValue + i
})
}, Math.round(Math.random() * 2))
}store.append(1, (currentValue) => currentValue + 1)
const currentData = await store.data()
assert(
currentData[1] === 121,
'Index 1 should be 121'
)
```## License
MIT