Merged in fix/delete-fuzzy (pull request #1538)

Support for delete keys fuzzy

* Support for delete keys fuzzy

* Added some logs


Approved-by: Anton Gunnarsson
This commit is contained in:
Linus Flood
2025-03-14 12:50:34 +00:00
parent c0b543f18d
commit f954deaf22

View File

@@ -7,87 +7,108 @@ const MIN_LENGTH = 1;
const QUERY_TYPE = t.Object({ key: t.String({ minLength: MIN_LENGTH }) }); const QUERY_TYPE = t.Object({ key: t.String({ minLength: MIN_LENGTH }) });
export const cacheRoutes = new Elysia({ prefix: "/cache" }) export const cacheRoutes = new Elysia({ prefix: "/cache" })
.get( .get(
"/", "/",
async ({ query: { key }, error }) => { async ({ query: { key }, error }) => {
key = validateKey(key); key = validateKey(key);
console.log("GET /cache", key); console.log("GET /cache", key);
const value = await redis.get(key); const value = await redis.get(key);
if (!value) { if (!value) {
return error("Not Found", "Not Found"); return error("Not Found", "Not Found");
} }
try { try {
const output = JSON.parse(value); const output = JSON.parse(value);
return { data: output }; return { data: output };
} catch (e) { } catch (e) {
redis.del(key); redis.del(key);
throw e; throw e;
} }
}, },
{ {
query: QUERY_TYPE, query: QUERY_TYPE,
response: { 200: t.Object({ data: t.Any() }), 404: t.String() }, response: { 200: t.Object({ data: t.Any() }), 404: t.String() },
} }
) )
.put( .put(
"/", "/",
async ({ query: { key }, body, error, set }) => { async ({ query: { key }, body, error, set }) => {
key = validateKey(key); key = validateKey(key);
console.log("PUT /cache", key); console.log("PUT /cache", key);
if (!body.ttl || body.ttl < 0) { if (!body.ttl || body.ttl < 0) {
return error("Bad Request", "ttl is required"); return error("Bad Request", "ttl is required");
} }
await redis.set(key, JSON.stringify(body.data), "EX", body.ttl); await redis.set(key, JSON.stringify(body.data), "EX", body.ttl);
set.status = 204; set.status = 204;
return; return;
}, },
{ {
body: t.Object({ data: t.Any(), ttl: t.Number() }), body: t.Object({ data: t.Any(), ttl: t.Number() }),
query: QUERY_TYPE, query: QUERY_TYPE,
response: { 204: t.Void(), 400: t.String() }, response: { 204: t.Void(), 400: t.String() },
} }
) )
.delete( .delete(
"/", "/",
async ({ query: { key, fuzzy }, set }) => { async ({ query: { key, fuzzy }, set }) => {
key = validateKey(key); key = validateKey(key);
console.log("DELETE /cache", key); console.log("DELETE /cache", key, { fuzzy });
if (fuzzy) { if (fuzzy) {
key = `*${key}*`; await deleteWithPattern(`*${key}*`);
} } else {
await redis.del(key);
console.log("Deleted key: ", key);
}
await redis.del(key); set.status = 204;
return;
set.status = 204; },
return; {
}, query: t.Object({
{ ...QUERY_TYPE.properties,
query: t.Object({ ...t.Object({ fuzzy: t.Optional(t.Boolean()) }).properties,
...QUERY_TYPE.properties, }),
...t.Object({ fuzzy: t.Optional(t.Boolean()) }).properties, response: { 204: t.Void(), 400: t.String() },
}), }
response: { 204: t.Void(), 400: t.String() }, );
}
);
function validateKey(key: string) { function validateKey(key: string) {
const parsedKey = decodeURIComponent(key); const parsedKey = decodeURIComponent(key);
if (parsedKey.length < MIN_LENGTH) { if (parsedKey.length < MIN_LENGTH) {
throw new ModelValidationError( throw new ModelValidationError("Key has to be atleast 1 character long");
"Key has to be atleast 1 character long" }
);
}
if (parsedKey.includes("*")) { if (parsedKey.includes("*")) {
throw new ModelValidationError("Key cannot contain wildcards"); throw new ModelValidationError("Key cannot contain wildcards");
} }
return parsedKey; return parsedKey;
}
async function deleteWithPattern(pattern: string) {
let cursor = "0";
let keys: string[] = [];
do {
const [newCursor, foundKeys] = await redis.scan(
cursor,
"MATCH",
pattern,
"COUNT",
5000
);
cursor = newCursor;
keys.push(...foundKeys);
} while (cursor !== "0");
if (keys.length > 0) {
await redis.del(...keys);
}
console.log("Deleted number of keys: ", keys.length);
} }