From b363433ab751e2c7cd8a897f0ddb9851f8681a8c Mon Sep 17 00:00:00 2001 From: Milho001 <l.milhomemfrancochristino@uu.nl> Date: Mon, 3 Feb 2025 11:03:18 +0000 Subject: [PATCH] feat: cache query result for one minute --- .env.example | 3 ++- src/readers/queryService.ts | 32 +++++++++++++++++++++++++++++++- src/variables.ts | 2 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index d3b1dc6..b74250a 100644 --- a/.env.example +++ b/.env.example @@ -17,4 +17,5 @@ SCHEMA_RETRIEVER=neo4j SMTP_HOST=smtp.office365.com SMTP_PORT=587 SMTP_USER= -SMTP_PASSWORD= \ No newline at end of file +SMTP_PASSWORD= +QUERY_CACHE_DURATION= \ No newline at end of file diff --git a/src/readers/queryService.ts b/src/readers/queryService.ts index d1b8f3a..9da7bad 100644 --- a/src/readers/queryService.ts +++ b/src/readers/queryService.ts @@ -1,6 +1,6 @@ import { graphQueryBackend2graphQuery, type DbConnection, type QueryRequest } from 'ts-common'; -import { rabbitMq, redis, ums, type QueryExecutionTypes } from '../variables'; +import { QUERY_CACHE_DURATION, rabbitMq, redis, ums, type QueryExecutionTypes } from '../variables'; import { log } from '../logger'; import { QueryPublisher } from '../utils/queryPublisher'; import { query2Cypher } from '../utils/cypher/converter'; @@ -12,6 +12,27 @@ import { RabbitMqBroker } from 'ts-common/rabbitMq'; import { Neo4jConnection } from 'ts-common/neo4j'; export const queryService = async (db: DbConnection, query: string): Promise<GraphQueryResultMetaFromBackend> => { + let index = 0; + const disambiguatedQuery = query.replace(/\d{13}/g, () => (index++).toString()); + const cacheKey = Bun.hash(JSON.stringify({ db: db, query: disambiguatedQuery })).toString(); + + if (QUERY_CACHE_DURATION === '') { + log.info('Query cache disabled, skipping cache check'); + } else { + // check for cached results + log.debug('Checking cache for query, with cache ttl', QUERY_CACHE_DURATION, 'seconds'); + const cached = await redis.client.get(cacheKey); + if (cached) { + log.info('Cache hit for query'); + log.debug('Cache hit for query', disambiguatedQuery); + const buf = Buffer.from(cached, 'base64'); + const inflated = Bun.gunzipSync(new Uint8Array(buf)); + const dec = new TextDecoder(); + const cachedMessage = JSON.parse(dec.decode(inflated)) as GraphQueryResultMetaFromBackend; + return cachedMessage; + } + } + // TODO: only neo4j is supported for now const connection = new Neo4jConnection(db); try { @@ -21,6 +42,15 @@ export const queryService = async (db: DbConnection, query: string): Promise<Gra // calculate metadata const result = graphQueryBackend2graphQuery(graph); + // cache result + const compressedMessage = Bun.gzipSync(JSON.stringify(result)); + const base64Message = Buffer.from(compressedMessage).toString('base64'); + + if (QUERY_CACHE_DURATION !== '') { + // if cache enabled, cache the result + await redis.setWithExpire(cacheKey, base64Message, QUERY_CACHE_DURATION); // ttl in seconds + } + return result; } catch (error) { log.error('Error parsing query result:', query, error); diff --git a/src/variables.ts b/src/variables.ts index 18c889b..e7f3f4a 100644 --- a/src/variables.ts +++ b/src/variables.ts @@ -20,6 +20,8 @@ export const REDIS_HOST = Bun.env.REDIS_HOST || 'localhost'; export const REDIS_PORT = parseInt(Bun.env.REDIS_PORT || '6379'); export const REDIS_PASSWORD = Bun.env.REDIS_PASSWORD || 'DevOnlyPass'; export const REDIS_SCHEMA_CACHE_DURATION = Bun.env.REDIS_SCHEMA_CACHE_DURATION || '60m'; +export const QUERY_CACHE_DURATION = + Bun.env.QUERY_CACHE_DURATION === '' || !Bun.env.QUERY_CACHE_DURATION ? '' : parseInt(Bun.env.QUERY_CACHE_DURATION); export const redis = new RedisConnector(REDIS_PASSWORD, REDIS_HOST, REDIS_PORT); -- GitLab