Skip to content

Commit e7635db

Browse files
authored
chore(logging): remove redis-progress-markers feature flag (#5287)
* chore(logging): remove redis-progress-markers feature flag Promote the Redis progress-marker write path to permanent behavior now that the flag is fully rolled out. Block markers always write to Redis (primary), keeping the durable jsonb_set UPDATE fallback when Redis is unavailable. Removes the flag registry entry, its REDIS_PROGRESS_MARKERS env fallback, and the per-session flag resolution in the logging session. * chore(logging): drop vestigial readProgressMarkers plumbing Follow-up to the redis-progress-markers flag removal. With the flag gone the completion fold always reads Redis markers, so the readProgressMarkers param (and its dead false branch) is removed end-to-end: - drop the param from completeWorkflowExecution and CompleteWorkflowExecutionParams - read progress markers unconditionally in the completion fold - delete the orphaned isFeatureEnabled mock and the flag-off / flag-throws marker tests (states the code can no longer produce)
1 parent 7bf8dba commit e7635db

6 files changed

Lines changed: 11 additions & 102 deletions

File tree

apps/sim/lib/core/config/env.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ export const env = createEnv({
7777
TABLE_SNAPSHOT_CACHE: z.boolean().optional(), // Mount tables into sandboxes by reference via a version-keyed CSV snapshot in object storage instead of draining the whole table into web-process heap
7878
PII_REDACTION: z.boolean().optional(), // Redact PII from workflow logs via configurable Data Retention rules (Presidio at the logger persist choke point) and expose the Data Retention config UI
7979
TRIGGER_EU_REGION: z.boolean().optional(), // Route Trigger.dev runs to eu-central-1 instead of the default us-east-1 (fallback for the trigger-eu-region flag when AppConfig is not the source of truth)
80-
REDIS_PROGRESS_MARKERS: z.boolean().optional(), // Write per-block live progress markers to Redis instead of jsonb_set UPDATEs on workflow_execution_logs (fallback for the redis-progress-markers flag when AppConfig is not the source of truth)
8180

8281
// Table feature limits (per plan). Apply when billing is disabled (free tier defaults) or for billed plans.
8382
FREE_TABLES_LIMIT: z.number().optional(), // Max user tables per workspace on free tier (default: 5)

apps/sim/lib/core/config/feature-flags.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,6 @@ const FEATURE_FLAGS = {
9797
'resolveTriggerRegion, so the whole deployment switches regions together.',
9898
fallback: 'TRIGGER_EU_REGION',
9999
},
100-
'redis-progress-markers': {
101-
description:
102-
'Write per-block live progress markers (lastStartedBlock/lastCompletedBlock) to Redis ' +
103-
'instead of jsonb_set UPDATEs on workflow_execution_logs, folding them into the single ' +
104-
'terminal UPDATE at completion. Eliminates the heaviest write query. Resolved once per ' +
105-
'logging session (no user/org context) so an execution never mixes write paths.',
106-
fallback: 'REDIS_PROGRESS_MARKERS',
107-
},
108100
'workspace-forking': {
109101
description:
110102
'Runtime rollout gate for workspace forking (fork/promote/rollback), layered on top of ' +

apps/sim/lib/logs/execution/logger.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,6 @@ export class ExecutionLogger implements IExecutionLoggerService {
679679
isResume?: boolean
680680
level?: 'info' | 'error'
681681
status?: 'completed' | 'failed' | 'cancelled' | 'pending'
682-
readProgressMarkers?: boolean
683682
}): Promise<WorkflowExecutionLog> {
684683
const {
685684
executionId,
@@ -695,7 +694,6 @@ export class ExecutionLogger implements IExecutionLoggerService {
695694
isResume,
696695
level: levelOverride,
697696
status: statusOverride,
698-
readProgressMarkers = true,
699697
} = params
700698

701699
let execLog = logger.withMetadata({ executionId })
@@ -753,7 +751,7 @@ export class ExecutionLogger implements IExecutionLoggerService {
753751
models: costSummary.models,
754752
}
755753

756-
const progressMarkers = readProgressMarkers ? await getProgressMarkers(executionId) : null
754+
const progressMarkers = await getProgressMarkers(executionId)
757755

758756
const builtExecutionData = this.buildCompletedExecutionData({
759757
existingExecutionData,

apps/sim/lib/logs/execution/logging-session.test.ts

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,17 @@ vi.mock('@/lib/logs/execution/logger', () => ({
6767
}))
6868

6969
const {
70-
isFeatureEnabledMock,
7170
setLastStartedBlockMock,
7271
setLastCompletedBlockMock,
7372
getProgressMarkersMock,
7473
clearProgressMarkersMock,
7574
} = vi.hoisted(() => ({
76-
isFeatureEnabledMock: vi.fn().mockResolvedValue(false),
7775
setLastStartedBlockMock: vi.fn().mockResolvedValue(false),
7876
setLastCompletedBlockMock: vi.fn().mockResolvedValue(false),
7977
getProgressMarkersMock: vi.fn().mockResolvedValue({}),
8078
clearProgressMarkersMock: vi.fn().mockResolvedValue(undefined),
8179
}))
8280

83-
vi.mock('@/lib/core/config/feature-flags', () => ({
84-
isFeatureEnabled: isFeatureEnabledMock,
85-
}))
86-
8781
vi.mock('@/lib/logs/execution/progress-markers', () => ({
8882
setLastStartedBlock: setLastStartedBlockMock,
8983
setLastCompletedBlock: setLastCompletedBlockMock,
@@ -720,8 +714,7 @@ describe('LoggingSession progress-marker write path', () => {
720714
dbMocks.execute.mockResolvedValue(undefined)
721715
})
722716

723-
it('writes markers to Redis (not the row) when the flag is on and Redis accepts the write', async () => {
724-
isFeatureEnabledMock.mockResolvedValue(true)
717+
it('writes markers to Redis (not the row) when Redis accepts the write', async () => {
725718
setLastStartedBlockMock.mockResolvedValue(true)
726719
setLastCompletedBlockMock.mockResolvedValue(true)
727720
const session = new LoggingSession('wf-1', 'exec-redis', 'manual', 'req-1')
@@ -741,8 +734,7 @@ describe('LoggingSession progress-marker write path', () => {
741734
expect(dbMocks.execute).not.toHaveBeenCalled()
742735
})
743736

744-
it('falls back to the SQL UPDATE when the flag is on but the Redis write fails', async () => {
745-
isFeatureEnabledMock.mockResolvedValue(true)
737+
it('falls back to the SQL UPDATE when the Redis write fails', async () => {
746738
setLastStartedBlockMock.mockResolvedValue(false)
747739
const session = new LoggingSession('wf-1', 'exec-redis-down', 'manual', 'req-1')
748740
await session.start({ workspaceId: 'ws-1' })
@@ -752,46 +744,4 @@ describe('LoggingSession progress-marker write path', () => {
752744
expect(setLastStartedBlockMock).toHaveBeenCalled()
753745
expect(dbMocks.execute).toHaveBeenCalledTimes(1)
754746
})
755-
756-
it('writes markers via jsonb_set UPDATE when the flag is off', async () => {
757-
isFeatureEnabledMock.mockResolvedValue(false)
758-
const session = new LoggingSession('wf-1', 'exec-sql', 'manual', 'req-1')
759-
await session.start({ workspaceId: 'ws-1' })
760-
761-
await session.onBlockStart('b1', 'Fetch', 'api', '2026-06-27T10:00:00.000Z')
762-
763-
expect(dbMocks.execute).toHaveBeenCalledTimes(1)
764-
expect(setLastStartedBlockMock).not.toHaveBeenCalled()
765-
})
766-
767-
it('falls back to the SQL path when flag resolution throws', async () => {
768-
isFeatureEnabledMock.mockRejectedValue(new Error('appconfig unavailable'))
769-
const session = new LoggingSession('wf-1', 'exec-fallback', 'manual', 'req-1')
770-
await session.start({ workspaceId: 'ws-1' })
771-
772-
await session.onBlockStart('b1', 'Fetch', 'api', '2026-06-27T10:00:00.000Z')
773-
774-
expect(dbMocks.execute).toHaveBeenCalledTimes(1)
775-
expect(setLastStartedBlockMock).not.toHaveBeenCalled()
776-
})
777-
778-
it('tells completion to read Redis markers only when the flag is on (no wasted ops when off)', async () => {
779-
completeWorkflowExecutionMock.mockResolvedValue({})
780-
781-
isFeatureEnabledMock.mockResolvedValue(true)
782-
const onSession = new LoggingSession('wf-1', 'exec-on', 'manual', 'req-1')
783-
await onSession.start({ workspaceId: 'ws-1' })
784-
await onSession.safeComplete({ finalOutput: { ok: true } })
785-
expect(completeWorkflowExecutionMock).toHaveBeenLastCalledWith(
786-
expect.objectContaining({ executionId: 'exec-on', readProgressMarkers: true })
787-
)
788-
789-
isFeatureEnabledMock.mockResolvedValue(false)
790-
const offSession = new LoggingSession('wf-1', 'exec-off', 'manual', 'req-1')
791-
await offSession.start({ workspaceId: 'ws-1' })
792-
await offSession.safeComplete({ finalOutput: { ok: true } })
793-
expect(completeWorkflowExecutionMock).toHaveBeenLastCalledWith(
794-
expect.objectContaining({ executionId: 'exec-off', readProgressMarkers: false })
795-
)
796-
})
797747
})

apps/sim/lib/logs/execution/logging-session.ts

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { createLogger } from '@sim/logger'
44
import { describeError, toError } from '@sim/utils/errors'
55
import { and, eq, sql } from 'drizzle-orm'
66
import { releaseExecutionSlot } from '@/lib/billing/calculations/usage-reservation'
7-
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
87
import { isRetryableInfrastructureError } from '@/lib/core/errors/retryable-infrastructure'
98
import { executionLogger } from '@/lib/logs/execution/logger'
109
import {
@@ -136,13 +135,6 @@ export class LoggingSession {
136135
private workflowState?: WorkflowState
137136
private correlation?: NonNullable<ExecutionTrigger['data']>['correlation']
138137
private isResume = false
139-
/**
140-
* Whether per-block progress markers go to Redis (vs jsonb_set UPDATEs on the
141-
* log row). Resolved once in {@link start} and cached so an execution never
142-
* mixes write paths across its block callbacks. Defaults to the legacy SQL
143-
* path until resolved.
144-
*/
145-
private useRedisMarkers = false
146138
private completed = false
147139
/** Synchronous flag to prevent concurrent completion attempts (race condition guard) */
148140
private completing = false
@@ -182,24 +174,12 @@ export class LoggingSession {
182174
}
183175

184176
/**
185-
* Resolve the per-block marker write path (Redis vs jsonb_set UPDATE) for this
186-
* session. Defaults to the legacy SQL path if flag resolution fails.
187-
*/
188-
private async resolveRedisMarkerMode(): Promise<boolean> {
189-
try {
190-
return await isFeatureEnabled('redis-progress-markers')
191-
} catch {
192-
return false
193-
}
194-
}
195-
196-
/**
197-
* Persist the last-started-block marker. Redis is the primary path when the
198-
* flag is on; falls back to the durable jsonb_set UPDATE when Redis is
199-
* unavailable or the write fails, so a marker is never dropped.
177+
* Persist the last-started-block marker. Redis is the primary path; falls back
178+
* to the durable jsonb_set UPDATE when Redis is unavailable or the write fails,
179+
* so a marker is never dropped.
200180
*/
201181
private async persistLastStartedBlock(marker: ExecutionLastStartedBlock): Promise<void> {
202-
if (this.useRedisMarkers && (await setLastStartedBlock(this.executionId, marker))) {
182+
if (await setLastStartedBlock(this.executionId, marker)) {
203183
return
204184
}
205185
try {
@@ -220,12 +200,12 @@ export class LoggingSession {
220200
}
221201

222202
/**
223-
* Persist the last-completed-block marker. Redis is the primary path when the
224-
* flag is on; falls back to the durable jsonb_set UPDATE when Redis is
225-
* unavailable or the write fails, so a marker is never dropped.
203+
* Persist the last-completed-block marker. Redis is the primary path; falls
204+
* back to the durable jsonb_set UPDATE when Redis is unavailable or the write
205+
* fails, so a marker is never dropped.
226206
*/
227207
private async persistLastCompletedBlock(marker: ExecutionLastCompletedBlock): Promise<void> {
228-
if (this.useRedisMarkers && (await setLastCompletedBlock(this.executionId, marker))) {
208+
if (await setLastCompletedBlock(this.executionId, marker)) {
229209
return
230210
}
231211
try {
@@ -308,7 +288,6 @@ export class LoggingSession {
308288
isResume: this.isResume,
309289
level: params.level,
310290
status: params.status,
311-
readProgressMarkers: this.useRedisMarkers,
312291
})
313292

314293
// Release the admission reservation from preprocessing. Skipped on pause: a
@@ -356,8 +335,6 @@ export class LoggingSession {
356335
} = params
357336

358337
try {
359-
this.useRedisMarkers = await this.resolveRedisMarkerMode()
360-
361338
this.trigger = createTriggerObject(this.triggerType, triggerData)
362339
this.correlation = triggerData?.correlation
363340
this.environment = createEnvironmentObject(

apps/sim/lib/logs/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -463,12 +463,5 @@ export interface ExecutionLoggerService {
463463
isResume?: boolean
464464
level?: 'info' | 'error'
465465
status?: 'completed' | 'failed' | 'cancelled' | 'pending'
466-
/**
467-
* Whether this session wrote live progress markers to Redis. When false, the
468-
* completion fold skips the Redis read/clear entirely (markers are already on
469-
* the row via the SQL path). Defaults to true so non-session callers keep the
470-
* safe read-and-fold behavior.
471-
*/
472-
readProgressMarkers?: boolean
473466
}): Promise<WorkflowExecutionLog>
474467
}

0 commit comments

Comments
 (0)