fix: remove old event queue, cleaned up session handling, remove hacks
This commit is contained in:
@@ -25,7 +25,7 @@ export class SessionBuffer extends BaseBuffer {
|
||||
this.redis = getRedisCache();
|
||||
}
|
||||
|
||||
async getExistingSession(sessionId: string) {
|
||||
public async getExistingSession(sessionId: string) {
|
||||
const hit = await this.redis.get(`session:${sessionId}`);
|
||||
|
||||
if (hit) {
|
||||
|
||||
@@ -69,7 +69,8 @@ export type INotificationRuleCached = Awaited<
|
||||
ReturnType<typeof getNotificationRulesByProjectId>
|
||||
>[number];
|
||||
export const getNotificationRulesByProjectId = cacheable(
|
||||
function getNotificationRulesByProjectId(projectId: string) {
|
||||
'getNotificationRulesByProjectId',
|
||||
(projectId: string) => {
|
||||
return db.notificationRule.findMany({
|
||||
where: {
|
||||
projectId,
|
||||
@@ -330,6 +331,17 @@ export async function checkNotificationRulesForEvent(
|
||||
);
|
||||
}
|
||||
|
||||
const isFunnelRule = (rule: INotificationRuleCached) =>
|
||||
rule.config.type === 'funnel';
|
||||
|
||||
export function getHasFunnelRules(rules: INotificationRuleCached[]) {
|
||||
return rules.some(isFunnelRule);
|
||||
}
|
||||
|
||||
export function getFunnelRules(rules: INotificationRuleCached[]) {
|
||||
return rules.filter(isFunnelRule);
|
||||
}
|
||||
|
||||
export async function checkNotificationRulesForSessionEnd(
|
||||
events: IServiceEvent[],
|
||||
) {
|
||||
@@ -344,8 +356,7 @@ export async function checkNotificationRulesForSessionEnd(
|
||||
getNotificationRulesByProjectId(projectId),
|
||||
]);
|
||||
|
||||
const funnelRules = rules.filter((rule) => rule.config.type === 'funnel');
|
||||
|
||||
const funnelRules = getFunnelRules(rules);
|
||||
const notificationPromises = funnelRules.flatMap((rule) => {
|
||||
// Match funnel events
|
||||
let funnelIndex = 0;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"@openpanel/logger": "workspace:*",
|
||||
"@openpanel/redis": "workspace:*",
|
||||
"bullmq": "^5.8.7",
|
||||
"groupmq": "1.0.0-next.17"
|
||||
"groupmq": "1.0.0-next.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openpanel/sdk": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Queue, QueueEvents } from 'bullmq';
|
||||
|
||||
import type { IServiceEvent, Prisma } from '@openpanel/db';
|
||||
import type {
|
||||
IServiceCreateEventPayload,
|
||||
IServiceEvent,
|
||||
Prisma,
|
||||
} from '@openpanel/db';
|
||||
import { createLogger } from '@openpanel/logger';
|
||||
import { getRedisGroupQueue, getRedisQueue } from '@openpanel/redis';
|
||||
import type { TrackPayload } from '@openpanel/sdk';
|
||||
@@ -32,16 +36,10 @@ export interface EventsQueuePayloadCreateEvent {
|
||||
type: 'createEvent';
|
||||
payload: Omit<IServiceEvent, 'id'>;
|
||||
}
|
||||
type SessionEndRequired =
|
||||
| 'sessionId'
|
||||
| 'deviceId'
|
||||
| 'profileId'
|
||||
| 'projectId'
|
||||
| 'createdAt';
|
||||
|
||||
export interface EventsQueuePayloadCreateSessionEnd {
|
||||
type: 'createSessionEnd';
|
||||
payload: Partial<Omit<IServiceEvent, SessionEndRequired>> &
|
||||
Pick<IServiceEvent, SessionEndRequired>;
|
||||
payload: IServiceCreateEventPayload;
|
||||
}
|
||||
|
||||
// TODO: Rename `EventsQueuePayloadCreateSessionEnd`
|
||||
@@ -95,18 +93,6 @@ export type MiscQueuePayload = MiscQueuePayloadTrialEndingSoon;
|
||||
|
||||
export type CronQueueType = CronQueuePayload['type'];
|
||||
|
||||
export const eventsQueue = new Queue<EventsQueuePayload>('events', {
|
||||
connection: getRedisQueue(),
|
||||
defaultJobOptions: {
|
||||
removeOnComplete: 10,
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: 'exponential',
|
||||
delay: 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const orderingWindowMs = Number.parseInt(
|
||||
process.env.ORDERING_WINDOW_MS || '50',
|
||||
10,
|
||||
|
||||
@@ -23,33 +23,57 @@ export async function getCache<T>(
|
||||
return data;
|
||||
}
|
||||
|
||||
export function cacheable<T extends (...args: any) => any>(
|
||||
fn: T,
|
||||
expireInSec: number,
|
||||
) {
|
||||
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();
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
export function cacheable<T extends (...args: any) => any>(
|
||||
fnOrName: T | string,
|
||||
fnOrExpireInSec: number | T,
|
||||
_expireInSec?: number,
|
||||
) {
|
||||
const name = typeof fnOrName === 'string' ? fnOrName : fnOrName.name;
|
||||
const fn =
|
||||
typeof fnOrName === 'function'
|
||||
? fnOrName
|
||||
: typeof fnOrExpireInSec === 'function'
|
||||
? fnOrExpireInSec
|
||||
: null;
|
||||
const expireInSec =
|
||||
typeof fnOrExpireInSec === 'number'
|
||||
? fnOrExpireInSec
|
||||
: typeof _expireInSec === 'number'
|
||||
? _expireInSec
|
||||
: null;
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('fn is not a function');
|
||||
}
|
||||
|
||||
if (typeof expireInSec !== 'number') {
|
||||
throw new Error('expireInSec is not a number');
|
||||
}
|
||||
|
||||
const cachePrefix = `cachable:${name}`;
|
||||
const getKey = (...args: Parameters<T>) =>
|
||||
`${cachePrefix}:${stringify(args)}`;
|
||||
const cachedFn = async (
|
||||
|
||||
Reference in New Issue
Block a user