diff options
| author | Adam Soltys <[email protected]> | 2024-08-26 21:17:43 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-08-26 21:17:43 -0700 |
| commit | fe62cadd6fb4a7e49f7d59401fc74ae0411e32a7 (patch) | |
| tree | aa8f34e1692bd2d638a2c996eeadb58671d07aea /lib/db.ts | |
| parent | 00e43ddc0bf3d6dd719fc9295d156567fa2fecfb (diff) | |
| download | coinos-server-fe62cadd6fb4a7e49f7d59401fc74ae0411e32a7.tar.gz coinos-server-fe62cadd6fb4a7e49f7d59401fc74ae0411e32a7.zip | |
typescript
Diffstat (limited to 'lib/db.ts')
| -rw-r--r-- | lib/db.ts | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/lib/db.ts b/lib/db.ts new file mode 100644 index 00000000..4f113a1d --- /dev/null +++ b/lib/db.ts @@ -0,0 +1,142 @@ +import config from "$config"; +import { fail, wait, sleep } from "$lib/utils"; +import { createClient, defineScript } from "redis"; +import { warn, l, err } from "$lib/logging"; + +let SCRIPT = ` +local balanceKey = KEYS[1] +local creditKey = KEYS[2] +local amount = tonumber(ARGV[1]) +local tip = tonumber(ARGV[2]) +local fee = tonumber(ARGV[3]) +local ourfee = tonumber(ARGV[4]) + +local balance = tonumber(redis.call('get', balanceKey) or '0') +local credit = tonumber(redis.call('get', creditKey) or '0') + +local covered = math.min(credit, ourfee) +ourfee = ourfee - covered + +if balance < amount + tip + fee + ourfee then + return {err = 'Insufficient funds ⚡️' .. balance .. ' of ⚡️' .. amount + tip + fee + ourfee} +end + +redis.call('decrby', creditKey, tostring(math.floor(covered))) +redis.call('decrby', balanceKey, tostring(math.floor(amount + tip + fee + ourfee))) + +return ourfee +`; + +let debit = defineScript({ + NUMBER_OF_KEYS: 2, + SCRIPT, + transformArguments: (...args) => args.map((a) => a.toString()), +}); + +export let db = createClient({ + url: config.db, + scripts: { debit }, + socket: { + reconnectStrategy: (retries) => Math.min(retries * 50, 5000), + }, +}); + +export let archive = createClient({ + url: config.archive, + socket: { + reconnectStrategy: (retries) => Math.min(retries * 50, 5000), + }, +}); + +export let arc2 = createClient({ + url: config.arc2, + socket: { + reconnectStrategy: (retries) => Math.min(retries * 50, 5000), + }, +}); + +async function dbReconnect() { + try { + await db.connect(); + } catch (err) { + console.error("Failed to connect to Redis, retrying...", err); + setTimeout(dbReconnect, 5000); // Retry after 5 seconds + } +} + +async function archiveReconnect() { + try { + await archive.connect(); + } catch (err) { + console.error("Failed to connect to Redis, retrying...", err); + setTimeout(archiveReconnect, 5000); // Retry after 5 seconds + } +} + +dbReconnect(); +archiveReconnect(); + +db.on("error", (e) => { + if (e.message.startsWith("getaddr")) return; + err("Redis error", e.message); +}); + +db.on("end", () => { + warn("Redis connection ended"); +}); + +export default db; + +export let g = async (k) => { + let v = await db.get(k); + try { + return JSON.parse(v); + } catch (e) { + return v; + } +}; + +export let s = (k, v) => { + if (k === "user:null" || k === "user:undefined") fail("null user"); + db.set(k, JSON.stringify(v)); +}; + +export let ga = async (k) => { + let v = await archive.get(k); + try { + return JSON.parse(v); + } catch (e) { + return v; + } +}; + +export let sa = (k, v) => { + if (k === "user:null" || k === "user:undefined") { + warn("###### NULL USER #######"); + console.trace(); + } + archive.set(k, JSON.stringify(v)); +}; + +let retries = {}; +export let t = async (k, f) => { + try { + await db.watch(k); + await f(await db.get(k), db); + } catch (err) { + if (!err.message.includes("watch")) throw err; + + let r = retries[k] || 0; + retries[k] = r + 1; + + if (r < 10) { + await sleep(100); + await t(k, f); + } else { + delete retries[k]; + fail("unable to obtain lock"); + } + } + + delete retries[k]; +}; |
