From beacf2d3d77e76ff1f56d5413bd47f320cb47405 Mon Sep 17 00:00:00 2001 From: Ali Al Dallal Date: Mon, 24 Aug 2026 19:54:50 -0400 Subject: [PATCH] build(deps): dbos-transact 1.2.0, kin-openapi 0.147.0, sqlite 1.57.0; run-duration guard tolerates DBOS ms quantization Supersedes Dependabot #412, which failed CI on two independent axes: 1. go-get-proxied v1.4.1 raises its go directive to 1.26, dragging this module's directive past CI's pinned go-version '1.25' (GOTOOLCHAIN=local) and failing every Go job at package load. Held at v1.4.0 with a dependabot.yml ignore naming the lift condition (CI's Go moving to 1.26); the other three bumps keep go 1.25.7. 2. DBOS 1.2.0's faster completion timing exposes a storage-quantization artifact: sysdb stores created_at rounded to the nearest millisecond but completed_at truncated, so a sub-millisecond run can read as completing 1ms before it started. Mill's runDuration treated any negative span as clock skew and dropped the run from AvgDuration (SampleSize undercounted, ~1 in 20 locally at -count=20). runDuration now accepts up to 1ms negative span, clamped to zero, with the boundary pinned in TestRunDuration's table (1ms valid, 2ms invalid). go test ./... green; golangci-lint clean on both tag sets; TestHomeMetrics_AvgDurationAndLastTriggeredAt_WiredFromRealRuns green at -count=25 under DBOS 1.2.0. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012im1JxQQV2ahnXzZDdVmZq --- .github/dependabot.yml | 7 +++++++ go.mod | 6 +++--- go.sum | 12 ++++++------ .../executionsvc/executionservice_home.go | 15 ++++++++++++--- .../executionsvc/executionservice_home_test.go | 5 +++++ 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index da78e2ce1..566a58ad4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,13 @@ updates: interval: weekly day: monday open-pull-requests-limit: 5 + ignore: + # go-get-proxied v1.4.1+ raises its go directive to 1.26, which + # drags this module's go directive past CI's pinned go-version + # '1.25' (GOTOOLCHAIN=local) and fails every Go job at package + # load. Lift when CI's Go moves to 1.26. + - dependency-name: github.com/rapid7/go-get-proxied + versions: [">= 1.4.1"] groups: go-dependencies: update-types: diff --git a/go.mod b/go.mod index 8d32641bd..8b6c5db05 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2 github.com/PuerkitoBio/goquery v1.12.0 github.com/andybalholm/cascadia v1.3.4 - github.com/dbos-inc/dbos-transact-golang v1.1.0 + github.com/dbos-inc/dbos-transact-golang v1.2.0 github.com/expr-lang/expr v1.17.8 github.com/fsnotify/fsnotify v1.10.1 - github.com/getkin/kin-openapi v0.146.0 + github.com/getkin/kin-openapi v0.147.0 github.com/go-jose/go-jose/v4 v4.1.4 github.com/google/uuid v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.8 @@ -30,7 +30,7 @@ require ( golang.org/x/oauth2 v0.36.0 golang.org/x/text v0.41.0 gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.56.0 + modernc.org/sqlite v1.57.0 ) require ( diff --git a/go.sum b/go.sum index 79242d732..72dd12a38 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dbos-inc/dbos-transact-golang v1.1.0 h1:J2uSNUDdfNx1Qbxg67k0cwm3xZ6xbtJ4SXIIyu33RZ8= -github.com/dbos-inc/dbos-transact-golang v1.1.0/go.mod h1:cOOJfxDEnhdZd39EpYWps4mva6JxKyyKztyWzGxXpCE= +github.com/dbos-inc/dbos-transact-golang v1.2.0 h1:o1CaHn+E7FQSBXJ/rUP7vV+yTYZB7vhzd98dnyxLlMA= +github.com/dbos-inc/dbos-transact-golang v1.2.0/go.mod h1:HEjtVW5wgpkpTIAp+FAIVldSr+oiiolyFZ+2RJda8gI= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -33,8 +33,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/getkin/kin-openapi v0.146.0 h1:RA/1RdxrSJW4oc1+6IfnYB6AO9CaGy8GTKPh0k4Ordo= -github.com/getkin/kin-openapi v0.146.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= +github.com/getkin/kin-openapi v0.147.0 h1:s+Xsm9gUMPJbgCnABZ2to3zSQQ5A9dyj/zo62VVsldY= +github.com/getkin/kin-openapi v0.147.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e h1:Lf/gRkoycfOBPa42vU2bbgPurFong6zXeFtPoxholzU= @@ -216,8 +216,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.56.0 h1:/D8e2RfFqoy/Zc6PuC76U28zFwmI/sYx1Kjm4yEn9e0= -modernc.org/sqlite v1.56.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ= +modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg= +modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/internal/services/executionsvc/executionservice_home.go b/internal/services/executionsvc/executionservice_home.go index 1763318ac..99f5d4854 100644 --- a/internal/services/executionsvc/executionservice_home.go +++ b/internal/services/executionsvc/executionservice_home.go @@ -305,13 +305,22 @@ func (e *ExecutionService) timeSavedFor(runs []RunSummary) TimeSavedMetric { // zero. CompletedAt exactly equal to StartedAt is a real, valid // zero-duration result -- confirmed against real DBOS timing (a purely // local, no-I/O step can genuinely complete within the same recorded -// millisecond) -- only CompletedAt strictly BEFORE StartedAt (clock +// millisecond). CompletedAt up to 1ms BEFORE StartedAt is also valid, +// clamped to zero: DBOS's sysdb stores created_at rounded to the +// nearest millisecond (Round(time.Millisecond).UnixMilli()) but +// completed_at truncated (UnixMilli()), so a run finishing within the +// same half-millisecond it was created can read as completing 1ms +// before it started. Only a gap beyond that quantization window (clock // skew, a checkpoint race) is treated as invalid. func runDuration(r RunSummary) (time.Duration, bool) { - if r.CompletedAt.IsZero() || r.CompletedAt.Before(r.StartedAt) { + if r.CompletedAt.IsZero() || r.CompletedAt.Before(r.StartedAt.Add(-time.Millisecond)) { return 0, false } - return r.CompletedAt.Sub(r.StartedAt), true + d := r.CompletedAt.Sub(r.StartedAt) + if d < 0 { + d = 0 + } + return d, true } func avgDurationFor(runs []RunSummary) AvgDurationMetric { diff --git a/internal/services/executionsvc/executionservice_home_test.go b/internal/services/executionsvc/executionservice_home_test.go index 8088ae997..af7f9d51e 100644 --- a/internal/services/executionsvc/executionservice_home_test.go +++ b/internal/services/executionsvc/executionservice_home_test.go @@ -132,6 +132,11 @@ func TestRunDuration_ExcludesInFlightAndNonPositiveSpans(t *testing.T) { {"completed", RunSummary{StartedAt: start, CompletedAt: start.Add(30 * time.Second)}, 30 * time.Second, true}, {"still in flight (zero CompletedAt)", RunSummary{StartedAt: start}, 0, false}, {"completed in the same instant (real, valid zero duration)", RunSummary{StartedAt: start, CompletedAt: start}, 0, true}, + // Regression: DBOS stores created_at rounded to the nearest ms but + // completed_at truncated, so a sub-millisecond run can read as + // completing 1ms before it started -- valid, clamped to zero. + {"completed 1ms before started (DBOS ms quantization)", RunSummary{StartedAt: start, CompletedAt: start.Add(-time.Millisecond)}, 0, true}, + {"completed 2ms before started (beyond quantization: clock skew)", RunSummary{StartedAt: start, CompletedAt: start.Add(-2 * time.Millisecond)}, 0, false}, {"completed before started (clock skew)", RunSummary{StartedAt: start, CompletedAt: start.Add(-time.Second)}, 0, false}, } for _, c := range cases {