Skip to content

Commit 7f67ffd

Browse files
Merge branch 'main' into TRI-4372
2 parents a3fc6dc + de31220 commit 7f67ffd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1132
-451
lines changed

.changeset/funny-emus-pay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Allow setting concurrencyLimit to null to signal removing the concurrency limit on the queue

.changeset/lazy-carpets-reply.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@trigger.dev/sdk": patch
3+
"@trigger.dev/core": patch
4+
---
5+
6+
Fixed issue with asResponse and withResponse not working on runs.retrieve

apps/webapp/app/env.server.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,96 @@ const EnvironmentSchema = z.object({
9191
REDIS_PASSWORD: z.string().optional(),
9292
REDIS_TLS_DISABLED: z.string().optional(),
9393

94+
RATE_LIMIT_REDIS_HOST: z
95+
.string()
96+
.optional()
97+
.transform((v) => v ?? process.env.REDIS_HOST),
98+
RATE_LIMIT_REDIS_READER_HOST: z
99+
.string()
100+
.optional()
101+
.transform((v) => v ?? process.env.REDIS_READER_HOST),
102+
RATE_LIMIT_REDIS_READER_PORT: z.coerce
103+
.number()
104+
.optional()
105+
.transform(
106+
(v) =>
107+
v ?? (process.env.REDIS_READER_PORT ? parseInt(process.env.REDIS_READER_PORT) : undefined)
108+
),
109+
RATE_LIMIT_REDIS_PORT: z.coerce
110+
.number()
111+
.optional()
112+
.transform((v) => v ?? (process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined)),
113+
RATE_LIMIT_REDIS_USERNAME: z
114+
.string()
115+
.optional()
116+
.transform((v) => v ?? process.env.REDIS_USERNAME),
117+
RATE_LIMIT_REDIS_PASSWORD: z
118+
.string()
119+
.optional()
120+
.transform((v) => v ?? process.env.REDIS_PASSWORD),
121+
RATE_LIMIT_REDIS_TLS_DISABLED: z.string().default(process.env.REDIS_TLS_DISABLED ?? "false"),
122+
RATE_LIMIT_REDIS_CLUSTER_MODE_ENABLED: z.string().default("0"),
123+
124+
CACHE_REDIS_HOST: z
125+
.string()
126+
.optional()
127+
.transform((v) => v ?? process.env.REDIS_HOST),
128+
CACHE_REDIS_READER_HOST: z
129+
.string()
130+
.optional()
131+
.transform((v) => v ?? process.env.REDIS_READER_HOST),
132+
CACHE_REDIS_READER_PORT: z.coerce
133+
.number()
134+
.optional()
135+
.transform(
136+
(v) =>
137+
v ?? (process.env.REDIS_READER_PORT ? parseInt(process.env.REDIS_READER_PORT) : undefined)
138+
),
139+
CACHE_REDIS_PORT: z.coerce
140+
.number()
141+
.optional()
142+
.transform((v) => v ?? (process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined)),
143+
CACHE_REDIS_USERNAME: z
144+
.string()
145+
.optional()
146+
.transform((v) => v ?? process.env.REDIS_USERNAME),
147+
CACHE_REDIS_PASSWORD: z
148+
.string()
149+
.optional()
150+
.transform((v) => v ?? process.env.REDIS_PASSWORD),
151+
CACHE_REDIS_TLS_DISABLED: z.string().default(process.env.REDIS_TLS_DISABLED ?? "false"),
152+
CACHE_REDIS_CLUSTER_MODE_ENABLED: z.string().default("0"),
153+
154+
PUBSUB_REDIS_HOST: z
155+
.string()
156+
.optional()
157+
.transform((v) => v ?? process.env.REDIS_HOST),
158+
PUBSUB_REDIS_READER_HOST: z
159+
.string()
160+
.optional()
161+
.transform((v) => v ?? process.env.REDIS_READER_HOST),
162+
PUBSUB_REDIS_READER_PORT: z.coerce
163+
.number()
164+
.optional()
165+
.transform(
166+
(v) =>
167+
v ?? (process.env.REDIS_READER_PORT ? parseInt(process.env.REDIS_READER_PORT) : undefined)
168+
),
169+
PUBSUB_REDIS_PORT: z.coerce
170+
.number()
171+
.optional()
172+
.transform((v) => v ?? (process.env.REDIS_PORT ? parseInt(process.env.REDIS_PORT) : undefined)),
173+
PUBSUB_REDIS_USERNAME: z
174+
.string()
175+
.optional()
176+
.transform((v) => v ?? process.env.REDIS_USERNAME),
177+
PUBSUB_REDIS_PASSWORD: z
178+
.string()
179+
.optional()
180+
.transform((v) => v ?? process.env.REDIS_PASSWORD),
181+
PUBSUB_REDIS_TLS_DISABLED: z.string().default(process.env.REDIS_TLS_DISABLED ?? "false"),
182+
PUBSUB_REDIS_CLUSTER_MODE_ENABLED: z.string().default("0"),
183+
94184
DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT: z.coerce.number().int().default(10),
95185
DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT: z.coerce.number().int().default(10),
96186
DEFAULT_DEV_ENV_EXECUTION_ATTEMPTS: z.coerce.number().int().positive().default(1),
@@ -232,6 +322,8 @@ const EnvironmentSchema = z.object({
232322
MARQS_CONCURRENCY_LIMIT_BIAS: z.coerce.number().default(0.75),
233323
MARQS_AVAILABLE_CAPACITY_BIAS: z.coerce.number().default(0.3),
234324
MARQS_QUEUE_AGE_RANDOMIZATION_BIAS: z.coerce.number().default(0.25),
325+
MARQS_REUSE_SNAPSHOT_COUNT: z.coerce.number().int().default(0),
326+
MARQS_MAXIMUM_ORG_COUNT: z.coerce.number().int().optional(),
235327

236328
PROD_TASK_HEARTBEAT_INTERVAL_MS: z.coerce.number().int().optional(),
237329

apps/webapp/app/redis.server.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Cluster, Redis, type ClusterNode, type ClusterOptions } from "ioredis";
2+
import { logger } from "./services/logger.server";
3+
4+
export type RedisWithClusterOptions = {
5+
host?: string;
6+
port?: number;
7+
username?: string;
8+
password?: string;
9+
tlsDisabled?: boolean;
10+
clusterMode?: boolean;
11+
clusterOptions?: Omit<ClusterOptions, "redisOptions">;
12+
keyPrefix?: string;
13+
};
14+
15+
export type RedisClient = Redis | Cluster;
16+
17+
export function createRedisClient(
18+
connectionName: string,
19+
options: RedisWithClusterOptions
20+
): Redis | Cluster {
21+
let redis: Redis | Cluster;
22+
23+
if (options.clusterMode) {
24+
const nodes: ClusterNode[] = [
25+
{
26+
host: options.host,
27+
port: options.port,
28+
},
29+
];
30+
31+
logger.debug("Creating a redis cluster client", {
32+
connectionName,
33+
host: options.host,
34+
port: options.port,
35+
});
36+
37+
redis = new Redis.Cluster(nodes, {
38+
...options.clusterOptions,
39+
redisOptions: {
40+
connectionName,
41+
keyPrefix: options.keyPrefix,
42+
username: options.username,
43+
password: options.password,
44+
enableAutoPipelining: true,
45+
...(options.tlsDisabled
46+
? {
47+
checkServerIdentity: () => {
48+
// disable TLS verification
49+
return undefined;
50+
},
51+
}
52+
: { tls: {} }),
53+
},
54+
dnsLookup: (address, callback) => callback(null, address),
55+
slotsRefreshTimeout: 10000,
56+
});
57+
} else {
58+
logger.debug("Creating a redis client", {
59+
connectionName,
60+
host: options.host,
61+
port: options.port,
62+
});
63+
64+
redis = new Redis({
65+
connectionName,
66+
host: options.host,
67+
port: options.port,
68+
username: options.username,
69+
password: options.password,
70+
enableAutoPipelining: true,
71+
keyPrefix: options.keyPrefix,
72+
...(options.tlsDisabled ? {} : { tls: {} }),
73+
});
74+
}
75+
76+
redis.on("error", (error) => {
77+
logger.error("Redis client error", { connectionName, error });
78+
});
79+
80+
return redis;
81+
}

apps/webapp/app/services/apiRateLimit.server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { Duration } from "./rateLimiter.server";
55

66
export const apiRateLimiter = authorizationRateLimitMiddleware({
77
redis: {
8-
port: env.REDIS_PORT,
9-
host: env.REDIS_HOST,
10-
username: env.REDIS_USERNAME,
11-
password: env.REDIS_PASSWORD,
12-
enableAutoPipelining: true,
13-
...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
8+
port: env.RATE_LIMIT_REDIS_PORT,
9+
host: env.RATE_LIMIT_REDIS_HOST,
10+
username: env.RATE_LIMIT_REDIS_USERNAME,
11+
password: env.RATE_LIMIT_REDIS_PASSWORD,
12+
tlsDisabled: env.RATE_LIMIT_REDIS_TLS_DISABLED === "true",
13+
clusterMode: env.RATE_LIMIT_REDIS_CLUSTER_MODE_ENABLED === "1",
1414
},
1515
keyPrefix: "api",
1616
defaultLimiter: {

apps/webapp/app/services/authorizationRateLimitMiddleware.server.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { env } from "~/env.server";
99
import { logger } from "./logger.server";
1010
import { createRedisRateLimitClient, Duration, RateLimiter } from "./rateLimiter.server";
1111
import { RedisCacheStore } from "./unkey/redisCacheStore.server";
12+
import { RedisWithClusterOptions } from "~/redis.server";
1213

1314
const DurationSchema = z.custom<Duration>((value) => {
1415
if (typeof value !== "string") {
@@ -54,7 +55,7 @@ export type RateLimiterConfig = z.infer<typeof RateLimiterConfig>;
5455
type LimitConfigOverrideFunction = (authorizationValue: string) => Promise<unknown>;
5556

5657
type Options = {
57-
redis?: RedisOptions;
58+
redis?: RedisWithClusterOptions;
5859
keyPrefix: string;
5960
pathMatchers: (RegExp | string)[];
6061
pathWhiteList?: (RegExp | string)[];
@@ -163,12 +164,12 @@ export function authorizationRateLimitMiddleware({
163164

164165
const redisClient = createRedisRateLimitClient(
165166
redis ?? {
166-
port: env.REDIS_PORT,
167-
host: env.REDIS_HOST,
168-
username: env.REDIS_USERNAME,
169-
password: env.REDIS_PASSWORD,
170-
enableAutoPipelining: true,
171-
...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
167+
port: env.RATE_LIMIT_REDIS_PORT,
168+
host: env.RATE_LIMIT_REDIS_HOST,
169+
username: env.RATE_LIMIT_REDIS_USERNAME,
170+
password: env.RATE_LIMIT_REDIS_PASSWORD,
171+
tlsDisabled: env.RATE_LIMIT_REDIS_TLS_DISABLED === "true",
172+
clusterMode: env.RATE_LIMIT_REDIS_CLUSTER_MODE_ENABLED === "1",
172173
}
173174
);
174175

apps/webapp/app/services/platform.v3.server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ function initializePlatformCache() {
3939
const redisCacheStore = new RedisCacheStore({
4040
connection: {
4141
keyPrefix: "tr:cache:platform:v3",
42-
port: env.REDIS_PORT,
43-
host: env.REDIS_HOST,
44-
username: env.REDIS_USERNAME,
45-
password: env.REDIS_PASSWORD,
46-
enableAutoPipelining: true,
47-
...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
42+
port: env.CACHE_REDIS_PORT,
43+
host: env.CACHE_REDIS_HOST,
44+
username: env.CACHE_REDIS_USERNAME,
45+
password: env.CACHE_REDIS_PASSWORD,
46+
tlsDisabled: env.CACHE_REDIS_TLS_DISABLED === "true",
47+
clusterMode: env.CACHE_REDIS_CLUSTER_MODE_ENABLED === "1",
4848
},
4949
});
5050

apps/webapp/app/services/rateLimiter.server.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Ratelimit } from "@upstash/ratelimit";
2-
import Redis, { RedisOptions } from "ioredis";
2+
import { RedisOptions } from "ioredis";
33
import { env } from "~/env.server";
4+
import { createRedisClient, RedisWithClusterOptions } from "~/redis.server";
45
import { logger } from "./logger.server";
56

67
type Options = {
@@ -28,12 +29,12 @@ export class RateLimiter {
2829
redisClient ??
2930
createRedisRateLimitClient(
3031
redis ?? {
31-
port: env.REDIS_PORT,
32-
host: env.REDIS_HOST,
33-
username: env.REDIS_USERNAME,
34-
password: env.REDIS_PASSWORD,
35-
enableAutoPipelining: true,
36-
...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
32+
port: env.RATE_LIMIT_REDIS_PORT,
33+
host: env.RATE_LIMIT_REDIS_HOST,
34+
username: env.RATE_LIMIT_REDIS_USERNAME,
35+
password: env.RATE_LIMIT_REDIS_PASSWORD,
36+
tlsDisabled: env.RATE_LIMIT_REDIS_TLS_DISABLED === "true",
37+
clusterMode: env.RATE_LIMIT_REDIS_CLUSTER_MODE_ENABLED === "1",
3738
}
3839
),
3940
limiter,
@@ -70,8 +71,10 @@ export class RateLimiter {
7071
}
7172
}
7273

73-
export function createRedisRateLimitClient(redisOptions: RedisOptions): RateLimiterRedisClient {
74-
const redis = new Redis(redisOptions);
74+
export function createRedisRateLimitClient(
75+
redisOptions: RedisWithClusterOptions
76+
): RateLimiterRedisClient {
77+
const redis = createRedisClient("trigger:rateLimiter", redisOptions);
7578

7679
return {
7780
sadd: async <TData>(key: string, ...members: TData[]): Promise<number> => {

apps/webapp/app/services/realtimeClient.server.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import Redis, { Callback, Result, type RedisOptions } from "ioredis";
33
import { randomUUID } from "node:crypto";
44
import { longPollingFetch } from "~/utils/longPollingFetch";
55
import { logger } from "./logger.server";
6+
import { createRedisClient, RedisClient, RedisWithClusterOptions } from "~/redis.server";
67

78
export interface CachedLimitProvider {
89
getCachedLimit: (organizationId: string, defaultValue: number) => Promise<number | undefined>;
910
}
1011

1112
export type RealtimeClientOptions = {
1213
electricOrigin: string;
13-
redis: RedisOptions;
14+
redis: RedisWithClusterOptions;
1415
cachedLimitProvider: CachedLimitProvider;
1516
keyPrefix: string;
1617
expiryTimeInSeconds?: number;
@@ -26,12 +27,12 @@ export type RealtimeRunsParams = {
2627
};
2728

2829
export class RealtimeClient {
29-
private redis: Redis;
30+
private redis: RedisClient;
3031
private expiryTimeInSeconds: number;
3132
private cachedLimitProvider: CachedLimitProvider;
3233

3334
constructor(private options: RealtimeClientOptions) {
34-
this.redis = new Redis(options.redis);
35+
this.redis = createRedisClient("trigger:realtime", options.redis);
3536
this.expiryTimeInSeconds = options.expiryTimeInSeconds ?? 60 * 5; // default to 5 minutes
3637
this.cachedLimitProvider = options.cachedLimitProvider;
3738
this.#registerCommands();

apps/webapp/app/services/realtimeClientGlobal.server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ function initializeRealtimeClient() {
88
electricOrigin: env.ELECTRIC_ORIGIN,
99
keyPrefix: "tr:realtime:concurrency",
1010
redis: {
11-
port: env.REDIS_PORT,
12-
host: env.REDIS_HOST,
13-
username: env.REDIS_USERNAME,
14-
password: env.REDIS_PASSWORD,
15-
enableAutoPipelining: true,
16-
...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
11+
port: env.RATE_LIMIT_REDIS_PORT,
12+
host: env.RATE_LIMIT_REDIS_HOST,
13+
username: env.RATE_LIMIT_REDIS_USERNAME,
14+
password: env.RATE_LIMIT_REDIS_PASSWORD,
15+
tlsDisabled: env.RATE_LIMIT_REDIS_TLS_DISABLED === "true",
16+
clusterMode: env.RATE_LIMIT_REDIS_CLUSTER_MODE_ENABLED === "1",
1717
},
1818
cachedLimitProvider: {
1919
async getCachedLimit(organizationId, defaultValue) {

apps/webapp/app/services/unkey/redisCacheStore.server.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
import { Err, Ok, type Result } from "@unkey/error";
2-
import type { Entry, Store } from "@unkey/cache/stores";
3-
import type { RedisOptions } from "ioredis";
4-
import { Redis } from "ioredis";
51
import { CacheError } from "@unkey/cache";
2+
import type { Entry, Store } from "@unkey/cache/stores";
3+
import { Err, Ok, type Result } from "@unkey/error";
4+
import { createRedisClient, RedisClient, RedisWithClusterOptions } from "~/redis.server";
65

76
export type RedisCacheStoreConfig = {
8-
connection: RedisOptions;
7+
connection: RedisWithClusterOptions;
98
};
109

1110
export class RedisCacheStore<TNamespace extends string, TValue = any>
1211
implements Store<TNamespace, TValue>
1312
{
1413
public readonly name = "redis";
15-
private readonly redis: Redis;
14+
private readonly redis: RedisClient;
1615

1716
constructor(config: RedisCacheStoreConfig) {
18-
this.redis = new Redis(config.connection);
17+
this.redis = createRedisClient("trigger:cacheStore", config.connection);
1918
}
2019

2120
private buildCacheKey(namespace: TNamespace, key: string): string {

0 commit comments

Comments
 (0)