diff --git a/README.md b/README.md index 4c94e4a..23ad0df 100644 --- a/README.md +++ b/README.md @@ -365,6 +365,12 @@ Originally this library was implemented using [hashes](https://redis.io/docs/dat Since we need to do that all the time, we prefer to use key/value. Also this approach allow to customize serializer/deserializer, which is JSON by default. +## Are writting operations atomic? + +No, writes operatoins are not atomic because there are very few use cases where that matters. **openkey** is designed to process a constant stream of requests, where the only thing important to control reaching the limit of each plan. + +In case you need it, you can combine **openkey** with [superlock](https://github.com/Kikobeats/superlock), check the following [example](https://github.com/microlinkhq/openkey/blob/9df977877e5066478020332bffb0c1677a5cd89e/test/usage.js#L115-L138). + # License **openkey** © [microlink.io](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/openkey/blob/master/LICENSE.md) License.
diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000..57edd1a Binary files /dev/null and b/dump.rdb differ diff --git a/examples/server/package.json b/examples/server/package.json index 3e41a90..9763c0b 100644 --- a/examples/server/package.json +++ b/examples/server/package.json @@ -1,5 +1,9 @@ { "scripts": { "dev": "watchexec --on-busy-update=restart --exts js,yml --clear=clear 'node index.js'" + }, + "dependencies": { + "http-body": "latest", + "send-http": "latest" } } diff --git a/package.json b/package.json index 901d275..afb7bb9 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,9 @@ }, "keywords": [], "dependencies": { - "http-body": "~1.0.11", + "json-buffer": "~3.0.1", "mri": "~1.2.0", - "ms": "~2.1.3", - "send-http": "~1.0.6" + "ms": "~2.1.3" }, "devDependencies": { "@commitlint/cli": "latest", @@ -62,7 +61,8 @@ "postcss-focus": "latest", "simple-git-hooks": "latest", "standard": "latest", - "standard-version": "latest" + "standard-version": "latest", + "superlock": "latet" }, "engines": { "node": ">= 18" diff --git a/test/usage.js b/test/usage.js index 369124d..7fb0086 100644 --- a/test/usage.js +++ b/test/usage.js @@ -1,6 +1,7 @@ 'use strict' const { setTimeout } = require('timers/promises') +const { withLock } = require('superlock') const { randomUUID } = require('crypto') const Redis = require('ioredis') const test = require('ava') @@ -110,3 +111,28 @@ test(".increment # don't increment more than the limit", async t => { t.is(usage.remaining, 0) } }) + +test('.increment # handle race conditions (using superlock)', async t => { + const lock = withLock() + + const plan = await openkey.plans.create({ + id: randomUUID(), + limit: 1000, + period: '100ms' + }) + const key = await openkey.keys.create({ plan: plan.id }) + + await Promise.all( + [...Array(100).keys()].map(() => + lock(async () => { + const { pending, ...usage } = await openkey.usage.increment(key.value) + await pending + return usage + }) + ) + ) + + const usage = await openkey.usage(key.value) + t.is(usage.limit, 1000) + t.is(usage.remaining, 900) +})