You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(OUT-3686): switch ORDERBY back to Id ASC for full stability
CreateTime DESC closed the common race (concurrent updates) but left a
narrower one open (concurrent CREATE prepending to the front of the
cursor). Id ASC closes both: Id is monotonic and immutable, so any
customer added during the walk lands at the END of the cursor where
we'll still encounter it.
Cost: newly-created customers land on the last page instead of page 1,
so drift recovery for a fresh customer in a 10k realm walks all pages
(~5s) instead of hitting on page 1 (~500ms). The perf cost is bounded
and only fires on local-DB-miss paths — full stability is the priority.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
// (new to us → no local mapping), so DESC by creation time also keeps the
317
-
// common case fast (page-1 hit, ~500ms vs full-realm walk).
310
+
// ORDERBY Id ASC pins the cursor to a stable, append-only key. QBO's default
311
+
// ordering is MetaData.LastUpdatedTime DESC — under that ordering, a customer
312
+
// updated mid-walk shifts to the front and can push an unscanned row past
313
+
// our STARTPOSITION cursor (false negative). Id is monotonic and immutable,
314
+
// so concurrent updates do not move rows and any customer created during the
315
+
// walk lands at the end of the cursor where we'll still encounter it.
318
316
//
319
-
// Tradeoff: a customer CREATED in QBO during the walk would prepend to the
320
-
// front and slip past our cursor. This is an accepted miss class — same
321
-
// shape as the parallel-webhook TOCTOU we already accept elsewhere in this
322
-
// codebase, and bounded to a single ~5s window per drift-recovery call.
317
+
// Tradeoff vs. CreateTime DESC: newly-created customers land on the LAST
318
+
// page rather than page 1, so drift recovery for a fresh customer in a 10k
319
+
// realm walks all pages (~5s) instead of hitting on page 1 (~500ms). The
320
+
// perf cost is bounded and acceptable; full stability is the priority.
323
321
async_getCustomerByEmail(
324
322
email: string,
325
323
sanitizedCompanyName: string|undefined,
@@ -336,7 +334,7 @@ export default class IntuitAPI {
336
334
letstartPosition=1
337
335
338
336
while(true){
339
-
constcustomerQuery=`SELECT Id, SyncToken, Active, CompanyName, PrimaryEmailAddr FROM Customer WHERE Active IN (true, false) ORDERBY MetaData.CreateTime DESC STARTPOSITION ${startPosition} MAXRESULTS ${pageSize}`
337
+
constcustomerQuery=`SELECT Id, SyncToken, Active, CompanyName, PrimaryEmailAddr FROM Customer WHERE Active IN (true, false) ORDERBY Id ASC STARTPOSITION ${startPosition} MAXRESULTS ${pageSize}`
0 commit comments