AK-21/Graphite-Industrial-Intelligence
0
1import { Redis } from "@upstash/redis";2import { Cache } from "../core/index.js";3import { entityKind, is } from "../../entity.js";4import { OriginalName, Table } from "../../index.js";5const getByTagScript = `6local tagsMapKey = KEYS[1] -- tags map key7local tag = ARGV[1] -- tag8 9local compositeTableName = redis.call('HGET', tagsMapKey, tag)10if not compositeTableName then11 return nil12end13 14local value = redis.call('HGET', compositeTableName, tag)15return value16`;17const onMutateScript = `18local tagsMapKey = KEYS[1] -- tags map key19local tables = {} -- initialize tables array20local tags = ARGV -- tags array21 22for i = 2, #KEYS do23 tables[#tables + 1] = KEYS[i] -- add all keys except the first one to tables24end25 26if #tags > 0 then27 for _, tag in ipairs(tags) do28 if tag ~= nil and tag ~= '' then29 local compositeTableName = redis.call('HGET', tagsMapKey, tag)30 if compositeTableName then31 redis.call('HDEL', compositeTableName, tag)32 end33 end34 end35 redis.call('HDEL', tagsMapKey, unpack(tags))36end37 38local keysToDelete = {}39 40if #tables > 0 then41 local compositeTableNames = redis.call('SUNION', unpack(tables))42 for _, compositeTableName in ipairs(compositeTableNames) do43 keysToDelete[#keysToDelete + 1] = compositeTableName44 end45 for _, table in ipairs(tables) do46 keysToDelete[#keysToDelete + 1] = table47 end48 redis.call('DEL', unpack(keysToDelete))49end50`;51class UpstashCache extends Cache {52 constructor(redis, config, useGlobally) {53 super();54 this.redis = redis;55 this.useGlobally = useGlobally;56 this.internalConfig = this.toInternalConfig(config);57 this.luaScripts = {58 getByTagScript: this.redis.createScript(getByTagScript, { readonly: true }),59 onMutateScript: this.redis.createScript(onMutateScript)60 };61 }62 static [entityKind] = "UpstashCache";63 /**64 * Prefix for sets which denote the composite table names for each unique table65 *66 * Example: In the composite table set of "table1", you may find67 * `${compositeTablePrefix}table1,table2` and `${compositeTablePrefix}table1,table3`68 */69 static compositeTableSetPrefix = "__CTS__";70 /**71 * Prefix for hashes which map hash or tags to cache values72 */73 static compositeTablePrefix = "__CT__";74 /**75 * Key which holds the mapping of tags to composite table names76 *77 * Using this tagsMapKey, you can find the composite table name for a given tag78 * and get the cache value for that tag:79 *80 * ```ts81 * const compositeTable = redis.hget(tagsMapKey, 'tag1')82 * console.log(compositeTable) // `${compositeTablePrefix}table1,table2`83 *84 * const cachevalue = redis.hget(compositeTable, 'tag1')85 */86 static tagsMapKey = "__tagsMap__";87 /**88 * Queries whose auto invalidation is false aren't stored in their respective89 * composite table hashes because those hashes are deleted when a mutation90 * occurs on related tables.91 *92 * Instead, they are stored in a separate hash with the prefix93 * `__nonAutoInvalidate__` to prevent them from being deleted when a mutation94 */95 static nonAutoInvalidateTablePrefix = "__nonAutoInvalidate__";96 luaScripts;97 internalConfig;98 strategy() {99 return this.useGlobally ? "all" : "explicit";100 }101 toInternalConfig(config) {102 return config ? {103 seconds: config.ex,104 hexOptions: config.hexOptions105 } : {106 seconds: 1107 };108 }109 async get(key, tables, isTag = false, isAutoInvalidate) {110 if (!isAutoInvalidate) {111 const result2 = await this.redis.hget(UpstashCache.nonAutoInvalidateTablePrefix, key);112 return result2 === null ? void 0 : result2;113 }114 if (isTag) {115 const result2 = await this.luaScripts.getByTagScript.exec([UpstashCache.tagsMapKey], [key]);116 return result2 === null ? void 0 : result2;117 }118 const compositeKey = this.getCompositeKey(tables);119 const result = (await this.redis.hget(compositeKey, key)) ?? void 0;120 return result === null ? void 0 : result;121 }122 async put(key, response, tables, isTag = false, config) {123 const isAutoInvalidate = tables.length !== 0;124 const pipeline = this.redis.pipeline();125 const ttlSeconds = config && config.ex ? config.ex : this.internalConfig.seconds;126 const hexOptions = config && config.hexOptions ? config.hexOptions : this.internalConfig?.hexOptions;127 if (!isAutoInvalidate) {128 if (isTag) {129 pipeline.hset(UpstashCache.tagsMapKey, { [key]: UpstashCache.nonAutoInvalidateTablePrefix });130 pipeline.hexpire(UpstashCache.tagsMapKey, key, ttlSeconds, hexOptions);131 }132 pipeline.hset(UpstashCache.nonAutoInvalidateTablePrefix, { [key]: response });133 pipeline.hexpire(UpstashCache.nonAutoInvalidateTablePrefix, key, ttlSeconds, hexOptions);134 await pipeline.exec();135 return;136 }137 const compositeKey = this.getCompositeKey(tables);138 pipeline.hset(compositeKey, { [key]: response });139 pipeline.hexpire(compositeKey, key, ttlSeconds, hexOptions);140 if (isTag) {141 pipeline.hset(UpstashCache.tagsMapKey, { [key]: compositeKey });142 pipeline.hexpire(UpstashCache.tagsMapKey, key, ttlSeconds, hexOptions);143 }144 for (const table of tables) {145 pipeline.sadd(this.addTablePrefix(table), compositeKey);146 }147 await pipeline.exec();148 }149 async onMutate(params) {150 const tags = Array.isArray(params.tags) ? params.tags : params.tags ? [params.tags] : [];151 const tables = Array.isArray(params.tables) ? params.tables : params.tables ? [params.tables] : [];152 const tableNames = tables.map((table) => is(table, Table) ? table[OriginalName] : table);153 const compositeTableSets = tableNames.map((table) => this.addTablePrefix(table));154 await this.luaScripts.onMutateScript.exec([UpstashCache.tagsMapKey, ...compositeTableSets], tags);155 }156 addTablePrefix = (table) => `${UpstashCache.compositeTableSetPrefix}${table}`;157 getCompositeKey = (tables) => `${UpstashCache.compositeTablePrefix}${tables.sort().join(",")}`;158}159function upstashCache({ url, token, config, global = false }) {160 const redis = new Redis({161 url,162 token163 });164 return new UpstashCache(redis, config, global);165}166export {167 UpstashCache,168 upstashCache169};170//# sourceMappingURL=cache.js.map