diff --git a/plugin-server/src/config/config.ts b/plugin-server/src/config/config.ts index e6aecf0832d4b..ef013bc22a9fb 100644 --- a/plugin-server/src/config/config.ts +++ b/plugin-server/src/config/config.ts @@ -130,6 +130,7 @@ export function getDefaultConfig(): PluginsServerConfig { MAX_TEAM_ID_TO_BUFFER_ANONYMOUS_EVENTS_FOR: 0, USE_KAFKA_FOR_SCHEDULED_TASKS: true, CLOUD_DEPLOYMENT: null, + EXTERNAL_REQUEST_TIMEOUT_MS: 10 * 1000, // 10 seconds STARTUP_PROFILE_DURATION_SECONDS: 300, // 5 minutes STARTUP_PROFILE_CPU: false, diff --git a/plugin-server/src/main/ingestion-queues/on-event-handler-consumer.ts b/plugin-server/src/main/ingestion-queues/on-event-handler-consumer.ts index 752e13c1a01aa..7a5bc3799d373 100644 --- a/plugin-server/src/main/ingestion-queues/on-event-handler-consumer.ts +++ b/plugin-server/src/main/ingestion-queues/on-event-handler-consumer.ts @@ -94,7 +94,8 @@ export const startAsyncWebhooksHandlerConsumer = async ({ organizationManager, new Set(serverConfig.FETCH_HOSTNAME_GUARD_TEAMS.split(',').filter(String).map(Number)), appMetrics, - statsd + statsd, + serverConfig.EXTERNAL_REQUEST_TIMEOUT_MS ) const concurrency = serverConfig.TASKS_PER_WORKER || 20 diff --git a/plugin-server/src/types.ts b/plugin-server/src/types.ts index 5c8c747c8f6ee..22242dc00f4ba 100644 --- a/plugin-server/src/types.ts +++ b/plugin-server/src/types.ts @@ -203,6 +203,7 @@ export interface PluginsServerConfig { EVENT_OVERFLOW_BUCKET_REPLENISH_RATE: number /** Label of the PostHog Cloud environment. Null if not running PostHog Cloud. @example 'US' */ CLOUD_DEPLOYMENT: string | null + EXTERNAL_REQUEST_TIMEOUT_MS: number // dump profiles to disk, covering the first N seconds of runtime STARTUP_PROFILE_DURATION_SECONDS: number diff --git a/plugin-server/src/worker/ingestion/hooks.ts b/plugin-server/src/worker/ingestion/hooks.ts index d6648933c2389..218a6c71d41f1 100644 --- a/plugin-server/src/worker/ingestion/hooks.ts +++ b/plugin-server/src/worker/ingestion/hooks.ts @@ -263,7 +263,7 @@ export class HookCommander { fetchHostnameGuardTeams: Set | null /** Hook request timeout in ms. */ - EXTERNAL_REQUEST_TIMEOUT = 10 * 1000 + EXTERNAL_REQUEST_TIMEOUT: number constructor( postgres: PostgresRouter, @@ -271,7 +271,8 @@ export class HookCommander { organizationManager: OrganizationManager, fetchHostnameGuardTeams: Set | null = new Set(), appMetrics: AppMetrics, - statsd: StatsD | undefined + statsd: StatsD | undefined, + timeout: number ) { this.postgres = postgres this.teamManager = teamManager @@ -285,6 +286,7 @@ export class HookCommander { } this.statsd = statsd this.appMetrics = appMetrics + this.EXTERNAL_REQUEST_TIMEOUT = timeout } public async findAndFireHooks(event: PostIngestionEvent, actionMatches: Action[]): Promise { diff --git a/plugin-server/tests/main/ingestion-queues/each-batch.test.ts b/plugin-server/tests/main/ingestion-queues/each-batch.test.ts index 0580f53d2724b..ed40b3c98f691 100644 --- a/plugin-server/tests/main/ingestion-queues/each-batch.test.ts +++ b/plugin-server/tests/main/ingestion-queues/each-batch.test.ts @@ -214,7 +214,11 @@ describe('eachBatchX', () => { const hookCannon = new HookCommander( queue.pluginsServer.postgres, queue.pluginsServer.teamManager, - queue.pluginsServer.organizationManager + queue.pluginsServer.organizationManager, + new Set(), + queue.pluginsServer.appMetrics, + undefined, + queue.pluginsServer.EXTERNAL_REQUEST_TIMEOUT_MS ) const matchSpy = jest.spyOn(actionMatcher, 'match') // mock hasWebhooks to return true diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts b/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts index 71643e2668b48..0b65c4563594f 100644 --- a/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts +++ b/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts @@ -45,7 +45,15 @@ describe('Event Pipeline integration test', () => { actionManager = new ActionManager(hub.db.postgres) await actionManager.prepare() actionMatcher = new ActionMatcher(hub.db.postgres, actionManager) - hookCannon = new HookCommander(hub.db.postgres, hub.teamManager, hub.organizationManager) + hookCannon = new HookCommander( + hub.db.postgres, + hub.teamManager, + hub.organizationManager, + new Set(hub.FETCH_HOSTNAME_GUARD_TEAMS.split(',').filter(String).map(Number)), + hub.appMetrics, + undefined, + hub.EXTERNAL_REQUEST_TIMEOUT_MS + ) jest.spyOn(hub.db, 'fetchPerson') jest.spyOn(hub.db, 'createPerson') diff --git a/plugin-server/tests/worker/ingestion/hooks.test.ts b/plugin-server/tests/worker/ingestion/hooks.test.ts index 8f8a1337c0276..47f70a8b04ff8 100644 --- a/plugin-server/tests/worker/ingestion/hooks.test.ts +++ b/plugin-server/tests/worker/ingestion/hooks.test.ts @@ -491,8 +491,9 @@ describe('hooks', () => { {} as any, {} as any, new Set([hook.team_id]), // Hostname guard enabled - // mock object with queueError function as no-op - { queueError: () => Promise.resolve(), queueMetric: () => Promise.resolve() } as AppMetrics + { queueError: () => Promise.resolve(), queueMetric: () => Promise.resolve() } as unknown as AppMetrics, + undefined, + 20000 ) }) @@ -517,7 +518,7 @@ describe('hooks', () => { ), headers: { 'Content-Type': 'application/json' }, method: 'POST', - timeout: 10000, + timeout: 20000, }) }) @@ -556,7 +557,7 @@ describe('hooks', () => { ), headers: { 'Content-Type': 'application/json' }, method: 'POST', - timeout: 10000, + timeout: 20000, }) })