add better cache for getProfileId

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-08-08 22:02:13 +02:00
committed by Carl-Gerhard Lindesvärd
parent 7d703bcd80
commit b02cbb2946
3 changed files with 44 additions and 6 deletions

View File

@@ -11,7 +11,7 @@ import {
toISOString, toISOString,
} from '@openpanel/common'; } from '@openpanel/common';
import type { IServiceCreateEventPayload, IServiceEvent } from '@openpanel/db'; import type { IServiceCreateEventPayload, IServiceEvent } from '@openpanel/db';
import { createEvent } from '@openpanel/db'; import { createEvent, getProfileIdCached } from '@openpanel/db';
import { getLastScreenViewFromProfileId } from '@openpanel/db/src/services/event.service'; import { getLastScreenViewFromProfileId } from '@openpanel/db/src/services/event.service';
import { eventsQueue, findJobByPrefix, sessionsQueue } from '@openpanel/queue'; import { eventsQueue, findJobByPrefix, sessionsQueue } from '@openpanel/queue';
import type { import type {
@@ -47,7 +47,10 @@ export async function incomingEvent(job: Job<EventsQueuePayloadIncomingEvent>) {
}; };
// this will get the profileId from the alias table if it exists // this will get the profileId from the alias table if it exists
const profileId = body.profileId ? String(body.profileId) : ''; const profileId = await getProfileIdCached({
profileId: body.profileId,
projectId,
});
const createdAt = new Date(body.timestamp); const createdAt = new Date(body.timestamp);
const url = getProperty('__path'); const url = getProperty('__path');
const { path, hash, query, origin } = parsePath(url); const { path, hash, query, origin } = parsePath(url);

View File

@@ -180,6 +180,7 @@ export async function createProfileAlias({
alias: string; alias: string;
profileId: string; profileId: string;
}) { }) {
await getProfileIdCached.clear({ profileId, projectId });
await ch.insert({ await ch.insert({
table: TABLE_NAMES.alias, table: TABLE_NAMES.alias,
format: 'JSONEachRow', format: 'JSONEachRow',
@@ -221,7 +222,7 @@ export async function getProfileId({
profileId, profileId,
projectId, projectId,
}: { }: {
profileId: string | undefined; profileId: number | string | undefined;
projectId: string; projectId: string;
}) { }) {
if (!profileId) { if (!profileId) {
@@ -240,7 +241,7 @@ export async function getProfileId({
return res[0].profile_id; return res[0].profile_id;
} }
return profileId; return String(profileId);
} }
export const getProfileIdCached = cacheable(getProfileId, 60 * 30); export const getProfileIdCached = cacheable(getProfileId, 60 * 30);

View File

@@ -4,11 +4,36 @@ export function cacheable<T extends (...args: any) => any>(
fn: T, fn: T,
expireInSec: number expireInSec: number
) { ) {
return async function ( const cachePrefix = `cachable:${fn.name}`;
function stringify(obj: unknown): string {
if (obj === null) return 'null';
if (obj === undefined) return 'undefined';
if (typeof obj === 'boolean') return obj ? 'true' : 'false';
if (typeof obj === 'number') return String(obj);
if (typeof obj === 'string') return obj;
if (typeof obj === 'function') return obj.toString();
if (Array.isArray(obj)) {
return '[' + obj.map(stringify).join(',') + ']';
}
if (typeof obj === 'object') {
const pairs = Object.entries(obj)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => `${key}:${stringify(value)}`);
return pairs.join(':');
}
// Fallback for any other types
return String(obj);
}
const getKey = (...args: Parameters<T>) =>
`${cachePrefix}:${stringify(args)}`;
const cachedFn = async function (
...args: Parameters<T> ...args: Parameters<T>
): Promise<Awaited<ReturnType<T>>> { ): Promise<Awaited<ReturnType<T>>> {
// JSON.stringify here is not bullet proof since ordering of object keys matters etc // JSON.stringify here is not bullet proof since ordering of object keys matters etc
const key = `cachable:${fn.name}:${JSON.stringify(args)}`; const key = getKey(...args);
const cached = await getRedisCache().get(key); const cached = await getRedisCache().get(key);
if (cached) { if (cached) {
try { try {
@@ -25,4 +50,13 @@ export function cacheable<T extends (...args: any) => any>(
return result; return result;
}; };
cachedFn.getKey = getKey;
cachedFn.clear = async function (...args: Parameters<T>) {
const key = getKey(...args);
console.log('[cachable] Clear', key);
return getRedisCache().del(key);
};
return cachedFn;
} }