import { Redis } from "ioredis"; import { defaultReconnectOnError } from "@internal/redis"; import type { TaskTriggerSource } from "@trigger.dev/database"; import { env } from "~/env.server"; import { singleton } from "~/utils/singleton"; import { logger } from "./logger.server"; const KEY_PREFIX = "tids:"; type CachedTaskIdentifier = { s: string; ts: TaskTriggerSource; live: boolean; }; export type TaskIdentifierEntry = { slug: string; triggerSource: TaskTriggerSource; isInLatestDeployment: boolean; }; function buildKey(environmentId: string): string { return `${KEY_PREFIX}${environmentId}`; } function encode(entry: TaskIdentifierEntry): string { return JSON.stringify({ s: entry.slug, ts: entry.triggerSource, live: entry.isInLatestDeployment, } satisfies CachedTaskIdentifier); } function decode(raw: string): TaskIdentifierEntry { const parsed = JSON.parse(raw) as CachedTaskIdentifier; return { slug: parsed.s, triggerSource: parsed.ts, isInLatestDeployment: parsed.live, }; } function initializeRedis(): Redis | undefined { const host = env.CACHE_REDIS_HOST; if (!host) { return undefined; } return new Redis({ connectionName: "taskIdentifierCache", host, port: env.CACHE_REDIS_PORT, username: env.CACHE_REDIS_USERNAME, password: env.CACHE_REDIS_PASSWORD, keyPrefix: "tr:", enableAutoPipelining: true, reconnectOnError: defaultReconnectOnError, ...(env.CACHE_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }), }); } const redis = singleton("taskIdentifierCache", initializeRedis); export async function populateTaskIdentifierCache( environmentId: string, identifiers: TaskIdentifierEntry[] ): Promise { if (!redis) return; try { const key = buildKey(environmentId); const pipeline = redis.pipeline(); pipeline.del(key); if (identifiers.length > 0) { pipeline.sadd(key, ...identifiers.map(encode)); } await pipeline.exec(); } catch (error) { logger.error("Failed to populate task identifier cache", { environmentId, error, }); } } export async function invalidateTaskIdentifierCache(environmentId: string): Promise { if (!redis) return; try { const key = buildKey(environmentId); await redis.del(key); } catch (error) { logger.error("Failed to invalidate task identifier cache", { environmentId, error, }); } } export async function getTaskIdentifiersFromCache( environmentId: string ): Promise { if (!redis) return null; try { const key = buildKey(environmentId); const members = await redis.smembers(key); if (members.length === 0) return null; return members.map(decode); } catch (error) { logger.error("Failed to get task identifiers from cache", { environmentId, error, }); return null; } }