Compare commits
14 commits
79a8bf2664
...
64affde372
Author | SHA1 | Date | |
---|---|---|---|
64affde372 | |||
7151c77d15 | |||
6dfb8ce705 | |||
6648294566 | |||
eaaf62483c | |||
fe924df3d2 | |||
f767ab8483 | |||
aaed9e854d | |||
6de8f5bcdb | |||
2c42cf9dc8 | |||
146e05751d | |||
1906580fb4 | |||
37177e391d | |||
361ab5e8f4 |
5 changed files with 70 additions and 3 deletions
|
@ -8,7 +8,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
jobs:
|
jobs:
|
||||||
prettier:
|
boot-test:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
49
index.js
49
index.js
|
@ -68,6 +68,55 @@ async function get_counter_value(ns_name, key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function counter_increment(ns_name, key, offset, create = false) {
|
||||||
|
var nsid = await get_namespace_id(ns_name);
|
||||||
|
return await sql.begin(async (sql) => {
|
||||||
|
if (offset == 0) return true;
|
||||||
|
if (create) {
|
||||||
|
var existence_res =
|
||||||
|
await sql`SELECT value FROM keys WHERE name = ${key} AND namespace_id = ${nsid}`;
|
||||||
|
if (existence_res.length == 0) {
|
||||||
|
if (
|
||||||
|
!(await add_counter({
|
||||||
|
namespace: ns_name,
|
||||||
|
key: key,
|
||||||
|
value: 1,
|
||||||
|
enable_reset: false,
|
||||||
|
update_lowerbound: 0,
|
||||||
|
update_upperbound: 1,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
else return 1; // We know the key value will be 1 here, just return it explicitly (Ending early to save some SQL calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sql_res = await sql`UPDATE keys SET value = value + ${offset} WHERE
|
||||||
|
namespace_id = ${nsid} AND
|
||||||
|
name = ${key} AND
|
||||||
|
${offset < 0 ? sql`update_lowerbound <= ${offset}` : sql`update_upperbound >= ${offset}`}
|
||||||
|
RETURNING value`;
|
||||||
|
if (sql_res.length == 1) return parseInt(sql_res[0].value);
|
||||||
|
else return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get("/hit/:ns/:key", async function (req, res) {
|
||||||
|
var counter_res = await counter_increment(
|
||||||
|
req.params.ns,
|
||||||
|
req.params.key,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return res
|
||||||
|
.status(counter_res == null ? 403 : 200)
|
||||||
|
.json({ value: counter_res });
|
||||||
|
});
|
||||||
|
app.get("/hit/:key", async function (req, res) {
|
||||||
|
var counter_res = await counter_increment("default", req.params.key, 1, true);
|
||||||
|
return res
|
||||||
|
.status(counter_res == null ? 403 : 200)
|
||||||
|
.json({ value: counter_res });
|
||||||
|
});
|
||||||
app.get("/get/:ns/:key", async function (req, res) {
|
app.get("/get/:ns/:key", async function (req, res) {
|
||||||
var e = await get_counter_value(req.params.ns, req.params.key);
|
var e = await get_counter_value(req.params.ns, req.params.key);
|
||||||
return res.status(e == null ? 404 : 200).json({ value: e });
|
return res.status(e == null ? 404 : 200).json({ value: e });
|
||||||
|
|
|
@ -5,8 +5,8 @@ CREATE TABLE IF NOT EXISTS namespaces(
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS keys(
|
CREATE TABLE IF NOT EXISTS keys(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
namespace_id INTEGER REFERENCES namespaces (id) ON DELETE RESTRICT,
|
namespace_id INTEGER REFERENCES namespaces (id) ON DELETE RESTRICT NOT NULL,
|
||||||
name TEXT UNIQUE NOT NULL,
|
name TEXT NOT NULL,
|
||||||
-- Someone might want to store huge numbers in here, so... why not?
|
-- Someone might want to store huge numbers in here, so... why not?
|
||||||
value BIGINT NOT NULL DEFAULT 0,
|
value BIGINT NOT NULL DEFAULT 0,
|
||||||
enable_reset BOOLEAN NOT NULL DEFAULT TRUE,
|
enable_reset BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
|
10
migrations/0002-no-globally-unique-keys.sql
Normal file
10
migrations/0002-no-globally-unique-keys.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- 0002-no-globally-unique-keys.sql
|
||||||
|
-- This makes keys with the same name be able to exist in different namespaces, while still preventing duplicates in the same namespace.
|
||||||
|
BEGIN;
|
||||||
|
-- Delete the "unique" constraint
|
||||||
|
ALTER TABLE keys DROP CONSTRAINT IF EXISTS keys_name_key;
|
||||||
|
-- Avoid duplicate constraints.
|
||||||
|
ALTER TABLE keys DROP CONSTRAINT IF EXISTS keys_name_namespace_id_key;
|
||||||
|
-- Make sure that name and namespace_id together still have to be unique
|
||||||
|
ALTER TABLE keys ADD UNIQUE (name, namespace_id);
|
||||||
|
COMMIT;
|
8
migrations/0003-keys-namespace-id-not-null.sql
Normal file
8
migrations/0003-keys-namespace-id-not-null.sql
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
-- Make sure that keys.namespace_id isn't able to be null
|
||||||
|
BEGIN;
|
||||||
|
-- Remove all null keys.namespace_id values. Nuking data like this is reckless, but it's the only option.
|
||||||
|
DROP FROM keys WHERE namespace_id = NULL;
|
||||||
|
-- Delete before adding to avoid conflicts
|
||||||
|
ALTER TABLE keys ALTER COLUMN namespace_id DROP NOT NULL;
|
||||||
|
ALTER TABLE keys ALTER COLUMN namespace_id SET NOT NULL;
|
||||||
|
COMMIT;
|
Loading…
Reference in a new issue