Skip to content

indexeddb: Always consider versionupgrade events to be exclusive - #46610

Merged
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:indexeddb-versionchange
Jul 19, 2026
Merged

indexeddb: Always consider versionupgrade events to be exclusive#46610
mrobinson merged 1 commit into
servo:mainfrom
mrobinson:indexeddb-versionchange

Conversation

@mrobinson

@mrobinson mrobinson commented Jul 18, 2026

Copy link
Copy Markdown
Member

The specification says that versionupgrade events should always be
exclusive. The previous code was attempting to do that by setting the
list of scopes in transaction to all scopes, but that list can go stale
if new scopes are added. This extra check ensure that the
versionupgrade event continues to be truly exclusive.

Testing: This greatly decreases the flakiness of the IndexedDB tests for me
when run locally. This should fix three flaky tests on CI and probably more.
Fixes: #44200.
Fixes: #46603.
Fixes: #46123.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 18, 2026
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Jul 18, 2026
@github-actions github-actions Bot removed the T-linux-wpt Do a try run of the WPT label Jul 18, 2026
@github-actions

Copy link
Copy Markdown

🔨 Triggering try run (#29642114777) for Linux (WPT)

@github-actions

Copy link
Copy Markdown

Test results for linux-wpt from try job (#29642114777):

Flaky unexpected result (39)
  • OK /IndexedDB/idbdatabase_deleteObjectStore.any.worker.html (#43776)
    • PASS [expected FAIL] subtest: Deleted object store's name should be removed from database's list. Attempting to use a deleted IDBObjectStore should throw an InvalidStateError
  • TIMEOUT [expected OK] /_webgl/conformance2/sync/sync-webgl-specific.html
  • CRASH [expected OK] /_webgl/conformance2/wasm/readpixels-2gb-in-4gb-wasm-memory.html
  • TIMEOUT [expected OK] /cookies/attributes/domain.sub.html
    • TIMEOUT [expected FAIL] subtest: Empty domain treated as host cookie 2

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Empty domain treated as host cookie 3
    • NOTRUN [expected PASS] subtest: No host cookies returned for host cookies after non-host redirect 1
    • NOTRUN [expected PASS] subtest: No host cookies returned for host cookies after non-host redirect 2
    • NOTRUN [expected PASS] subtest: No host cookies returned for host cookies after non-host redirect 3
  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • FAIL [expected PASS] subtest: Matching font-stretch: '90%' should prefer '50% 80%' over '60% 70%'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 10deg' should prefer 'oblique 10deg' over 'oblique 5deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique 10deg' should prefer 'oblique 15deg 20deg' over 'oblique 30deg 60deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

    • FAIL [expected PASS] subtest: Matching font-style: 'oblique -10deg' should prefer 'oblique 0deg 10deg' over 'oblique 40deg 50deg'

      assert_equals: Unexpected font on test element expected 487 but got 532
      

  • FAIL [expected PASS] /css/css-sizing/dynamic-available-size-iframe.html (#45763)
  • OK /dom/nodes/moveBefore/iframe-document-preserve.window.html (#43152)
    • FAIL [expected PASS] subtest: moveBefore(): cross-origin iframe is preserved: remove self via innerHTML

      assert_equals: iframe does not fire a second load event expected 1 but got 0
      

  • OK [expected TIMEOUT] /fetch/api/redirect/redirect-keepalive.https.any.html (#32153)
    • PASS [expected TIMEOUT] subtest: [keepalive][iframe][load] mixed content redirect; setting up
  • TIMEOUT [expected OK] /fetch/content-encoding/br/big-br-body.https.any.html
    • TIMEOUT [expected PASS] subtest: large br data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large br data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/br/big-br-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large br data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large br data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/gzip/big-gzip-body.https.any.worker.html
    • TIMEOUT [expected PASS] subtest: large gzip data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large gzip data should be decompressed successfully with byte stream
  • TIMEOUT [expected OK] /fetch/content-encoding/zstd/big-zstd-body.https.any.html
    • TIMEOUT [expected PASS] subtest: large zstd data should be decompressed successfully

      Test timed out
      

    • NOTRUN [expected FAIL] subtest: large zstd data should be decompressed successfully with byte stream
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/multiple-iframes.https.window.html (#35176)
  • TIMEOUT [expected OK] /fetch/metadata/generated/css-font-face.https.sub.tentative.html
    • TIMEOUT [expected PASS] subtest: sec-fetch-site - Same site

      Test timed out
      

    • NOTRUN [expected PASS] subtest: sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect
    • NOTRUN [expected FAIL] subtest: sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Cross-Site -> Same Origin
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Cross-Site -> Same-Site
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Cross-Site -> Cross-Site
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Same-Origin -> Same Origin
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Same-Origin -> Same-Site
    • NOTRUN [expected PASS] subtest: sec-fetch-site - Same-Origin -> Cross-Site
    • NOTRUN [expected FAIL] subtest: sec-fetch-site - Same-Site -> Same Origin
    • And 7 more unexpected results...
  • ERROR [expected TIMEOUT] /html/browsers/browsing-the-web/history-traversal/pageswap/pageswap-initial-navigation.html (#40387)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • PASS [expected FAIL] subtest: load event does not fire on window.open('about:blank')
  • OK /html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html
    • FAIL [expected PASS] subtest: 0041 set in href="" targeting a frame and clicked

      assert_equals: expected "A" but got ""
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/refresh/same-document-refresh.html (#34597)
    • FAIL [expected PASS] subtest: Same-Document Referrer from Refresh

      assert_equals: original page loads expected "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh-with-section.sub.html?url=%23section#section"
      

  • TIMEOUT [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html (#44098)
    • TIMEOUT [expected FAIL] subtest: Replace before load, triggered by formElement.requestSubmit()

      Test timed out
      

  • OK [expected TIMEOUT] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html (#44099)
    • FAIL [expected TIMEOUT] subtest: Replace before load, triggered by submitButton.click()

      assert_equals: expected "http://web-platform.test:8000/common/blank.html?thereplacement=" but got "http://web-platform.test:8000/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html?pipe=sub(none)&code=%0A%20%20%20%20const%20form%20%3D%20document.createElement(%22form%22)%3B%0A%20%20%20%20form.action%20%3D%20%22%2Fcommon%2Fblank.html%22%3B%0A%0A%20%20%20%20const%20input%20%3D%20document.createElement(%22input%22)%3B%0A%20%20%20%20input.type%20%3D%20%22hidden%22%3B%0A%20%20%20%20input.name%20%3D%20%22thereplacement%22%3B%0A%20%20%20%20form.append(input)%3B%0A%0A%20%20%20%20const%20button%20%3D%20document.createElement(%22button%22)%3B%0A%20%20%20%20button.type%20%3D%20%22submit%22%3B%0A%20%20%20%20form.append(button)%3B%0A%0A%20%20%20%20document.currentScript.before(form)%3B%0A%20%20%20%20button.click()%3B%0A%20%20"
      

  • TIMEOUT [expected ERROR] /html/browsers/history/the-location-interface/location_replace_session_history.html (#41896)
  • OK /html/semantics/embedded-content/media-elements/media_fragment_seek.html (#24114)
    • FAIL [expected PASS] subtest: Video should seek to time specified in media fragment syntax

      assert_equals: expected 4 but got 0
      

  • ERROR [expected OK] /html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html (#33778)
  • OK /html/semantics/embedded-content/media-elements/seeking/seek-to-currentTime.html (#46014)
    • FAIL [expected PASS] subtest: seek to currentTime

      assert_array_equals: fired events lengths differ, expected array ["seeking", "timeupdate", "seeked"] length 3, got ["seeking", "timeupdate", "timeupdate", "seeked"] length 4
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html (#39702)
    • TIMEOUT [expected FAIL] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • CRASH [expected ERROR] /html/semantics/forms/the-select-element/customizable-select/select-appearance-button-after-span.html
  • TIMEOUT [expected OK] /html/syntax/parsing/html5lib_write_single.html?file=tests1 (#46327)
    • TIMEOUT [expected PASS] subtest: <!DOCTYPE html><font><table></font></table></font>

      Test timed out
      

  • OK /mixed-content/tentative/autoupgrades/audio-upgrade.https.sub.html (#41697)
    • FAIL [expected PASS] subtest: Audio of other host autoupgraded

      assert_equals: Length of other host audio is correct expected 1 but got Infinity
      

  • OK /mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html (#41135)
    • FAIL [expected PASS] subtest: Video of other host autoupgraded

      assert_equals: Length. Other host expected 1 but got Infinity
      

  • OK /paint-timing/fcp-only/fcp-svg.html (#45878)
    • PASS [expected FAIL] subtest: First contentful paint fires when SVG becomes contentful.
  • OK /preload/prefetch-document.html (#37210)
    • FAIL [expected PASS] subtest: different-site document prefetch with 'as=document' should not be consumed

      assert_equals: expected 2 but got 1
      

  • OK /preload/preload-resource-match.https.html
    • PASS [expected FAIL] subtest: Loading font (same-origin) with link (same-origin) should reuse the preloaded response
  • OK /resource-timing/buffer-full-then-increased.html (#44408)
    • PASS [expected FAIL] subtest: Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event
  • OK /touch-events/hover-state-caused-by-compatibility-mouse-events.tentative.html (#46277)
    • PASS [expected FAIL] subtest: The compatibility mouse events should set :hover: The compatibility mouse events should be fired
  • CRASH [expected OK] /trusted-types/TrustedTypePolicyFactory-createPolicy-unenforced.html
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • OK /upgrade-insecure-requests/gen/sharedworker-module-data.meta/unset/fetch.https.html
    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to cross-http-downgrade origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to cross-http-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to cross-https origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to same-http-downgrade origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to same-http-downgrade origin and no-redirect redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

    • FAIL [expected PASS] subtest: Upgrade-Insecure-Requests: Expects blocked for fetch to same-https origin and downgrade redirection from https context.

      assert_equals: The resource request should be 'blocked'. expected "blocked" but got "allowed"
      

  • OK /wasm/webapi/abort.any.html (#39966)
    • FAIL [expected PASS] subtest: compileStreaming() asynchronously racing with abort should succeed or reject with AbortError

      assert_equals: expected "AbortError" but got "CompileError"
      

  • OK /webxr/xrSession_features_deviceSupport.https.html (#24357)
    • FAIL [expected PASS] subtest: Immersive XRSession requests with no supported device should reject

      assert_unreached: Should have rejected: undefined Reached unreachable code
      

Stable unexpected results that are known to be intermittent (20)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in <script> tags.

      Test timed out
      

  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #85

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #87

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #89

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #91

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #101

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #103

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #105

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #107

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

  • OK /beacon/beacon-basic.https.window.html (#41723)
    • FAIL [expected PASS] subtest: Payload size restriction should be accumulated: type = string

      assert_false: expected false got true
      

    • PASS [expected FAIL] subtest: Payload size restriction should be accumulated: type = arraybuffer
  • OK /css/css-cascade/layer-font-face-override.html (#35935)
    • FAIL [expected PASS] subtest: @font-face override between layers

      assert_equals: expected "80px" but got "41.45px"
      

    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 1

      assert_equals: expected "80px" but got "41.45px"
      

    • FAIL [expected PASS] subtest: @font-face override update with appended sheet 2

      assert_equals: expected "80px" but got "41.45px"
      

  • PASS [expected FAIL] /css/css-text-decor/text-underline-position-from-font-variable.html (#45615)
  • OK [expected ERROR] /fetch/fetch-later/quota/same-origin-iframe/accumulated-oversized-payload.https.window.html (#41705)
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • TIMEOUT [expected PASS] subtest: background-image sec-fetch-site - Not sent to non-trustworthy same-origin destination

      Test timed out
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • FAIL [expected PASS] subtest: Same-origin navigation started from unload handler must be ignored

      assert_equals: expected "?pass" but got "?fail"
      

  • TIMEOUT [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html (#44028)
  • TIMEOUT [expected OK] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • OK /html/semantics/scripting-1/the-script-element/module/choice-of-error-1.html (#44058)
    • PASS [expected FAIL] subtest: Parse errors in different files should be reported depending on different roots
  • OK [expected TIMEOUT] /html/syntax/parsing/html5lib_write.html?file=processing-instructions (#46205)
  • OK /mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html (#41123)
    • FAIL [expected PASS] subtest: Cross-Origin video should get upgraded even if CORS is set

      assert_equals: Length. Other host expected 1 but got Infinity
      

  • OK /paint-timing/with-lcp/fcp-document-opacity-image.html (#43068)
    • PASS [expected FAIL] subtest: Test that FCP after opacity change is not a larger value than LCP
  • OK /pointerevents/pointerevent_pointercapture_in_frame.html?touch (#45445)
    • PASS [expected FAIL] subtest: Test touchpointer capture in same-origin frame: Pointer down at inner frame and set pointer capture.
    • PASS [expected FAIL] subtest: Test touchpointer capture in same-origin frame: Pointer down at inner frame and set pointer capture to outer frame should not capture.
  • OK /resource-timing/buffer-full-add-then-clear.html (#40819)
    • FAIL [expected PASS] subtest: Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one

      assert_equals: Number of entries does not match the expected value. expected 3 but got 0
      

  • OK [expected CRASH] /resource-timing/render-blocking-status-link.html (#41664)
    • FAIL [expected PASS] subtest: Validate render blocking status of link resources in PerformanceResourceTiming

      assert_equals: render blocking status for http://web-platform.test:8000/resource-timing/resources/empty_style.css?stylesheet-head-dynamic-docWrite should be blocking expected (string) "blocking" but got (undefined) undefined
      

  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?16-20 (#44835)
    • FAIL [expected PASS] subtest: Navigate a frame via area with javascript:-urls in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • TIMEOUT [expected PASS] subtest: Navigate a frame via area with javascript:-urls w/ default policy in report-only mode.

      Test timed out
      

  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.
    • TIMEOUT [expected NOTRUN] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.

      Test timed out
      

  • OK /visual-viewport/resize-event-order.html (#41981)
    • FAIL [expected PASS] subtest: Popup: DOMWindow resize fired before VisualViewport.

      assert_equals: expected "window-attribute,window-addEventListener,visualViewport-attribute,visualViewport-addEventListener" but got ""
      

@github-actions

Copy link
Copy Markdown

✨ Try run (#29642114777) succeeded.

Comment thread components/storage/indexeddb/mod.rs Outdated
// > ensure that only one connection to the database is open when an upgrade
// > transaction is live.
//
// This mean that versionupgrade transactions are always overlapping with othrs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling nit: others

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Jul 19, 2026
The specification says that `versionupgrade` events should always be
exclusive. The previous code was attempting to do that by setting the
list of scopes in transaction to all scopes, but that list can go stale
is new scopes are added. This extra check ensure that the
`versionupgrade` event continues to be truly exclusive.

Signed-off-by: Martin Robinson <martin@abandonedwig.info>
@mrobinson
mrobinson force-pushed the indexeddb-versionchange branch from 7f06667 to fac0034 Compare July 19, 2026 12:22
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Jul 19, 2026
@mrobinson
mrobinson enabled auto-merge July 19, 2026 12:22
@mrobinson
mrobinson added this pull request to the merge queue Jul 19, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Jul 19, 2026
Merged via the queue into servo:main with commit c8a434d Jul 19, 2026
35 checks passed
@mrobinson
mrobinson deleted the indexeddb-versionchange branch July 19, 2026 13:13
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-awaiting-review There is new code that needs to be reviewed.

Projects

None yet

3 participants