{"record":{"id":"31b3d94420c55abe","repo":"FlowiseAI/Flowise","slug":"time-sync-issue-with-database-updatedat-tim-31b3d9","errorCode":null,"errorMessage":"Time sync issue with database ${updatedAt} < ${timeAtLeast}","messagePattern":"Time sync issue with database (.+?) < (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts","lineNumber":313,"sourceCode":"            placeholders.push(`$${index * numOfColumns + i + 1}`)\n        }\n        return `(${placeholders.join(', ')})`\n    }\n\n    async update(keys: Array<{ uid: string; docId: string }> | string[], updateOptions?: UpdateOptions): Promise<void> {\n        if (keys.length === 0) {\n            return\n        }\n\n        const dataSource = await this.getDataSource()\n        const queryRunner = dataSource.createQueryRunner()\n        const tableName = this.sanitizeTableName(this.tableName)\n\n        const updatedAt = await this.getTime()\n        const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {}\n\n        if (timeAtLeast && updatedAt < timeAtLeast) {\n            throw new Error(`Time sync issue with database ${updatedAt} < ${timeAtLeast}`)\n        }\n\n        // Handle both new format (objects with uid and docId) and old format (strings)\n        const isNewFormat = keys.length > 0 && typeof keys[0] === 'object' && 'uid' in keys[0]\n        const keyStrings = isNewFormat ? (keys as Array<{ uid: string; docId: string }>).map((k) => k.uid) : (keys as string[])\n        const docIds = isNewFormat ? (keys as Array<{ uid: string; docId: string }>).map((k) => k.docId) : keys.map(() => null)\n\n        const groupIds = _groupIds ?? keyStrings.map(() => null)\n\n        if (groupIds.length !== keyStrings.length) {\n            throw new Error(`Number of keys (${keyStrings.length}) does not match number of group_ids ${groupIds.length})`)\n        }\n\n        const recordsToUpsert = keyStrings.map((key, i) => [key, this.namespace, updatedAt, groupIds[i], docIds[i]])\n\n        const valuesPlaceholders = recordsToUpsert.map((_, j) => this.generatePlaceholderForRowAt(j, recordsToUpsert[0].length)).join(', ')\n\n        const query = `INSERT INTO \"${tableName}\" (key, namespace, updated_at, group_id, doc_id) VALUES ${valuesPlaceholders} ON CONFLICT (key, namespace) DO UPDATE SET updated_at = EXCLUDED.updated_at, doc_id = EXCLUDED.doc_id;`","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/recordmanager/PostgresRecordManager/PostgresRecordManager.ts#L295-L331","documentation":"Postgres analog of error 263. Thrown in PostgresRecordManager.update when updateOptions.timeAtLeast is set and the database clock (this.getTime()) is older than that floor. Prevents writing backdated updated_at markers that would corrupt staleness-based cleanup.","triggerScenarios":"Postgres server clock behind the application server; NTP drift on the DB host; caller passed a future-dated timeAtLeast; cross-region setup with unsynchronized clocks.","commonSituations":"Cloud Postgres (RDS/Aurora/CloudSQL) failing NTP after maintenance; Docker container clock drift; CI environments with skewed clocks.","solutions":["Run NTP/chrony on both hosts; verify with SELECT NOW() versus the app clock.","Drop or relax timeAtLeast if your pipeline does not need the floor.","Confirm this.getTime() reads from the DB (SELECT NOW()), not the app.","Restart drifted DB instances or containers."],"exampleFix":"// before\nawait mgr.update(keys, { timeAtLeast: Date.now() })\n// after\nawait mgr.update(keys)","handlingStrategy":"retry","validationCode":"const dbNow = await manager.getTime()\nconst skew = Math.abs(Date.now() - dbNow)\nif (skew > 5_000) {\n  throw new Error(`App/Postgres clock skew ${skew}ms; sync NTP.`)\n}","typeGuard":"function isReasonableTimestamp(t: unknown, reference: number): t is number {\n  return typeof t === 'number' && t <= reference + 60_000\n}","tryCatchPattern":"try {\n  await manager.update(keys, { timeAtLeast })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Time sync issue')) {\n    // verify SELECT NOW() vs app; resync; retry once\n  }\n  throw e\n}","preventionTips":["Enable NTP on the Postgres host (RDS/Aurora do this automatically; self-hosted must).","Avoid passing timeAtLeast unless your dedup logic needs it.","Monitor DB-vs-app clock skew in production."],"tags":["postgres","record-manager","time-sync","clock-drift","ntp"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}