Skip to content

fix(engine) #5595: normalise a partition lookup key to the declared type before hashing it - #5598

Merged
lvca merged 3 commits into
mainfrom
issue-5595
Jul 30, 2026
Merged

fix(engine) #5595: normalise a partition lookup key to the declared type before hashing it#5598
lvca merged 3 commits into
mainfrom
issue-5595

Conversation

@lvca

@lvca lvca commented Jul 30, 2026

Copy link
Copy Markdown
Member

Closes #5595.

The defect

PartitionedBucketSelectionStrategy derives the bucket from Object.hashCode() on both sides, but the two sides saw differently boxed objects:

  • Placement (getBucketIdByRecord) hashes the value after the schema coerced it to the declared property type. Every write path funnels through MutableDocument.convertValueToSchemaType, so a LONG property always holds a Long.
  • Lookup (getBucketIdByKeys) hashed whatever the caller passed. TypeIndex hands over the raw keys, and the index's own convertKeys runs much later inside LSMTreeIndex.get.

Long.hashCode(v) is (int) (v ^ (v >>> 32)) while Integer.hashCode(v) is v, so the two agree only for positive values below 2^31. On a LONG partition key every negative value, and every value outside the int range, pruned to a bucket the record had never been placed in and the lookup silently found nothing.

This is not confined to the Java index API. A plain SELECT FROM T WHERE id = -5 hits it too: a SQL integer literal that fits an int arrives as an Integer, and SelectExecutionPlanner prunes buckets through the same strategy. That is the more visible symptom, and the issue did not mention it.

The fix

The lookup key is converted to the declared property type - the very coercion the write path applies - before being hashed, so both sides hash the same object. Placement is untouched, so no existing database needs a repartition.

Rather than inlining the coercion into the strategy, the mapping from a declared type to the Java class it is stored as now lives in one place, Type.getJavaImplementation(Database), and MutableDocument routes through it as well. The failure mode here was precisely two places independently deciding what a value is stored as; duplicating the DATE/DATETIME special-casing into the strategy would have recreated it.

I did not take the issue's stronger alternative (hashing serializeKeyForHashing bytes), for the reason the issue itself gives: it changes the placement function, so every existing partitioned database would need a repartition that REBUILD TYPE ... WITH repartition = true refuses on graph types.

Where it declines to prune

Returning -1 makes the caller fan out over every bucket: correct, only slower.

  • the partition property is not declared in the schema, so the record kept whatever Java type the writer used and there is no conversion target;
  • the key does not coerce to the declared type at all;
  • the partition index declares COLLATE CI - a sibling defect found while fixing this one, see below.

The COLLATE CI case

Case folding is an index-level normalisation that placement never applied, so 'Hello' and 'hello' are a single index key living in two different buckets. No lookup-side normalisation repairs that - only a change to placement could, which would force a repartition - so such a partition is no longer pruned.

Measured red/green with a 3-bucket type. Bucket counts that are a power of two up to 32 hide it by arithmetic accident: flipping the case of an ASCII letter shifts the Java string hash by a multiple of 32, so the low 5 bits survive.

Testing

New engine/src/test/java/com/arcadedb/partitioning/PartitionedBoxedKeyLookupTest.java, 7 tests. 3 were red on main (which already carries #5591); the round-robin control was green throughout. Each of the three declining paths above was verified red with the guard disabled and green with it.

Full engine module suite: 10351 tests, 0 failures, 23 skipped.

Two honest notes

  • The duplicate-admission half of the issue does not reproduce for a declared property. Both sides of the commit-time check (TransactionIndexContext.checkUniqueIndexKeys -> TypeIndex.get) read schema-coerced values, so they agree. The issue said it was inferred from the shared code path and not reproduced directly; that inference does not hold here. aUniquePartitionIndexRejectsADuplicateBoxedDifferently is kept as a regression guard, but it passed before the fix as well.
  • MutableDocument.convertValueToSchemaType is a very hot write path. The refactor is behaviour-preserving - for DATE/DATETIME the helper returns the same configured implementation class, which is never Document.class, so hoisting the embedded-document check out of the else is equivalent - and the full suite was run rather than just the partitioning tests because of it.

…ype before hashing it

`PartitionedBucketSelectionStrategy` derives the bucket from `Object.hashCode()` on both sides, but the two
sides saw differently boxed objects. Placement (`getBucketIdByRecord`) hashes the value AFTER the schema
coerced it to the declared property type; a lookup (`getBucketIdByKeys`) hashed whatever the caller passed,
because `TypeIndex` hands over the raw keys and the index's own `convertKeys` runs much later. Since
`Long.hashCode(v)` is `(int) (v ^ (v >>> 32))` while `Integer.hashCode(v)` is `v`, the two agree only for
positive values below 2^31: on a LONG partition key every negative value, and every value outside the int
range, pruned to a bucket the record had never been placed in and the lookup silently found nothing.

This was not confined to the Java index API. A plain `SELECT FROM T WHERE id = -5` hits it too, because a SQL
integer literal that fits an int arrives as an Integer and `SelectExecutionPlanner` prunes buckets through the
same strategy.

The lookup key is now converted to the declared property type - the very coercion the write path applies -
before being hashed, so both sides hash the same object. Placement is untouched, so no existing database needs
a repartition. The mapping from a declared type to the Java class it is stored as now lives in one place,
`Type.getJavaImplementation(Database)`, which `MutableDocument` also uses, so the two cannot drift apart.

Where the stored form cannot be reproduced the strategy declines to prune and the search fans out over every
bucket, which is correct and only slower:
- the partition property is not declared in the schema, so there is no conversion target;
- the key does not coerce to the declared type at all;
- the partition index declares COLLATE CI. Case folding is an index-level normalisation placement never
  applied, so 'Hello' and 'hello' are one index key living in two different buckets, and only a change to
  placement could reconcile that. Bucket counts that are a power of two up to 32 hid this by arithmetic
  accident: flipping the case of an ASCII letter shifts the Java string hash by a multiple of 32.

`PartitionedBoxedKeyLookupTest` covers all of it: 3 of its 7 tests were red on main, and the round-robin
control was green throughout. The duplicate-admission half of the report is NOT reproducible for a declared
property - both sides of the commit-time check read schema-coerced values - and the test that pins the
constraint passed before the fix as well.
@mergify

mergify Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review: #5595 - normalise a partition lookup key to the declared type before hashing

Overall this is a careful, high-quality fix with an excellent write-up. The root cause (placement hashing the schema-coerced value while lookup hashed the caller's raw box) is correctly diagnosed, and fixing the lookup side rather than placement, so existing databases don't need a repartition, is the right call. The three "decline to prune" paths (undeclared property, non-coercible key, COLLATE CI) all fail safe by fanning out. I verified the referenced methods (hasAnyCaseInsensitive, getPolymorphicPropertyIfExists, getMetadata, Type.convert) exist with the expected signatures.

Correctness

  • MutableDocument refactor is behaviour-preserving. For DATE/DATETIME, getJavaImplementation(database) returns the configured impl class, which is never Document.class, so hoisting the embedded-document check out of the old else branch is equivalent, as the PR claims. Type.convert is still called with the same javaImplementation, so the write path is unchanged.
  • Shared helper is the right structural move. Collapsing the "declared type -> stored Java class" mapping into Type.getJavaImplementation removes exactly the duplication that caused the bug; both sides now provably agree.
  • Alignment is correct. toStoredForm(lookupProperties.get(i), keyValues[i]) uses the lookup property name, not propertyNames.get(i), so it stays correct under the permuted-key case that coversPartitionProperties deliberately allows.

Test coverage - one real gap worth addressing

The 7 tests all assert records are found (via index.get(...).hasNext() or SQL). But fan-out (return -1) also finds every record. So these tests would still pass if the fix regressed to "always return -1" and never pruned at all - they prove correctness but not that pruning still selects the right single bucket. The only test that exercises -1 on purpose is the round-robin control.

Consider adding a direct assertion that, for a partitioned type, getBucketIdByKeys(props, {intKey}) returns the same bucket as getBucketIdByRecord(record) for the numerically-equal stored value (and that it is not -1). That pins the behaviour the fix actually delivers - pruning to the correct bucket - and guards against a silent regression into permanent fan-out, which is a performance cliff no functional test here would catch.

Minor test nit: in aLongKeyLookedUpAsAnIntegerFindsTheRecord, incrementing foundAsInteger for out-of-range values "to keep the tallies comparable" is a bit of a fudge; asserting per-value would read more clearly. Cosmetic.

Smaller notes (non-blocking)

  • toStoredForm catches Exception broadly. Coercion failure legitimately means "can't reproduce, fan out", but a swallowed RuntimeException from an unrelated bug would silently degrade to fan-out and hide the defect. On a correctness-critical path, narrowing the catch (or a debug-level log) would keep a future latent bug observable. Defensive as-is, so optional.
  • Per-query cost. partitionKeyIsCaseInsensitive() + toStoredForm() add a schema/index resolution and a Type.convert per getBucketIdByKeys call. Since this runs at prune time (~once per query, not per row) and Type.convert short-circuits on same-type values, the overhead is negligible and well justified. The Javadoc's reasoning for the deliberate no-cache choice on the CI check is nicely done.
  • Docs and comments are exemplary and make the invariant easy to maintain.

Verdict

Solid, well-reasoned, minimal-blast-radius fix. The main actionable item is strengthening the tests to assert pruning to the correct bucket rather than only record found, since the current suite cannot distinguish the fix from a regression to unconditional fan-out.

@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 9 complexity

Metric Results
Complexity 9

View in Codacy

🟢 Coverage 60.00% diff coverage · -6.70% coverage variation

Metric Results
Coverage variation -6.70% coverage variation
Diff coverage 60.00% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (f0f341d) 147814 110511 74.76%
Head commit (0a58a2c) 179828 (+32014) 122397 (+11886) 68.06% (-6.70%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#5598) 40 24 60.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

…chose, not just that the record is found

Addresses the code review's main point. Every assertion in the new suite was of the form "the record was found",
which fanning out over every bucket also satisfies - so the tests proved correctness but could not distinguish
the fix from a regression into answering -1 unconditionally and never pruning again, a silent performance cliff.

`anIntegerLookupKeyResolvesTheBucketPlacementChose` pins the invariant the fix actually delivers: for each stored
value, an Integer lookup key resolves the SAME bucket `getBucketIdByRecord` chose, is not -1, and still narrows
`getIndexesByKeys` to a single sub-index. Verified by simulating the regression - with the strategy forced to
return -1 the new test fails while the other seven still pass, which is exactly the gap the review identified.

The case-insensitive test now asserts the decline directly (`getBucketIdByKeys(...) == -1`) instead of inferring
it from a successful lookup, since fan-out is the only correct answer there and a decline is the only way to get it.

Also from the review:
- `aLongKeyLookedUpAsAnIntegerFindsTheRecord` asserts per value instead of counting matches and padding the
  tally for keys outside the int range, which read as a fudge.
- `toStoredForm` logs the coercion failure at FINE. The catch stays broad so no conversion failure can ever turn
  a lookup into a wrong answer, but an unrelated bug surfacing there now degrades visibly rather than silently
  costing every query a fan-out.
@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Code review

Reviewed the diff against the fix's stated design. This is a well-scoped, well-reasoned fix, and the write-up on the PR is genuinely excellent - the rationale (why normalise the lookup key rather than change placement) and the honest "does not reproduce" note on the duplicate-admission half are exactly right.

Correctness

  • The core fix is sound: getBucketIdByRecord hashes the schema-coerced stored value, and getBucketIdByKeys now replays that same coercion via toStoredForm before hashing, so both sides hash the same object. The UNKNOWN_STORED_FORM sentinel (distinct object, identity-compared, since null is a legitimate conversion result) is the right call.
  • Pairing is correct: coversPartitionProperties allows a permuted property set (sum-of-hashes is commutative), and the loop uses lookupProperties.get(i) with keyValues[i], so each value is coerced against its own property name even under permutation.
  • The MutableDocument.convertValueToSchemaType refactor is behaviour-preserving. The embedded-document check was previously only in the else branch, but hoisting it is safe because getJavaImplementation returns the date/datetime impl for DATE/DATETIME, which is never Document.class, so the check is a no-op for those types - same as before.
  • Centralising the DATE/DATETIME mapping in Type.getJavaImplementation is the right anti-duplication move given the failure mode was "two places independently deciding the stored form."
  • The COLLATE CI decline is a genuine sibling bug caught here, and declining (fan-out) is the only correct answer since only a repartition could reconcile it. Nice catch, and the deliberately non-power-of-two bucket count in the test that actually exposes it is a thoughtful detail.

Minor points (non-blocking)

  1. getJavaImplementation silent fallback for null/non-DatabaseInternal: when database isn't a DatabaseInternal, a DATE/DATETIME type silently returns javaDefaultType instead of the configured impl. Every current caller passes a DatabaseInternal (in MutableDocument the field is already DatabaseInternal, which is why the original direct getSerializer() call worked), so this is fine today and the javadoc documents it. Worth keeping in mind that a future caller passing null for a DATE property would get a divergent stored form with no error.

  2. Per-lookup cost: getBucketIdByKeys now does a getPolymorphicIndexByProperties map lookup plus, per key, a getPolymorphicPropertyIfExists + getSchema().getEmbedded().getDatabase() + Type.convert, on a hot query-pruning path. All individually cheap (HashMap lookups, idempotent Long->Long conversion), and correctness clearly justifies it - the PR acknowledges this. The database resolution inside toStoredForm could be hoisted out of the per-key loop, but the gain is negligible.

  3. Broad catch (Exception) at Level.FINE: correct defensive posture (never turn a conversion failure into a wrong bucket), and logging so a latent bug degrades visibly rather than silently. Just noting that if conversion started failing systematically, every query would silently fall back to a full fan-out and only whisper at FINE - the PR calls this out honestly.

Tests

Excellent coverage. The standout is anIntegerLookupKeyResolvesTheBucketPlacementChose and the direct -1 assertion in the CI test: they pin the real invariant (lookup resolves the same bucket placement chose, and still narrows to one sub-index) rather than "the record was found," which would pass even if the strategy regressed to unconditionally declining and never pruning again. The round-robin control and the INTEGER-property mirror case round it out well. Fast unit tests, no @Tag needed.

Overall: LGTM. The fix is minimal, correct, leaves placement untouched (so no repartition), and is backed by tests that guard the performance invariant and not just the visible symptom.

…stead of once per key

From the code review. `toStoredForm` walked `type.getSchema().getEmbedded().getDatabase()` on every key of the
lookup, but the same database backs all of them, so it is now resolved once in `getBucketIdByKeys` and passed in.

The gain is small - partition keys hold one to three properties - but this is a per-query pruning path and the
hoist costs nothing in clarity.
@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review: fix(engine) #5595 - normalise a partition lookup key to the declared type before hashing

Thorough, well-reasoned fix. The diagnosis (Long.hashCode vs Integer.hashCode diverging outside the positive-int range, placement hashing the schema-coerced value while lookup hashed the raw caller key) is correct, and the chosen remedy - normalise the lookup side and leave placement untouched so no repartition is forced - is the right trade-off. The PR body and Javadoc are exemplary; they explain not just what but why, including the declined alternatives.

Correctness - verified

  • MutableDocument.convertValueToSchemaType refactor is behaviour-preserving. Hoisting the javaImplementation.equals(Document.class) check out of the old else branch is safe: for DATE/DATETIME, getJavaImplementation(database) returns the configured date impl class, which is never Document.class, so the embedded-document branch is skipped exactly as before. Good call running the full suite given this is a hot write path.
  • Type.getJavaImplementation(Database) correctly centralises the DATE/DATETIME special-casing, and the null/!DatabaseInternal fallback to javaDefaultType is sound. In toStoredForm the database comes from type.getSchema().getEmbedded().getDatabase(), which is a DatabaseInternal at runtime, so the date path resolves correctly.
  • getMetadata() can return null (empty indexesOnBuckets); partitionKeyIsCaseInsensitive() guards for it. Good.
  • The UNKNOWN_STORED_FORM sentinel (distinct object vs null, since null is a legitimate conversion result) is the right pattern.

Test coverage

Excellent. anIntegerLookupKeyResolvesTheBucketPlacementChose is the standout: it pins the real invariant (lookup bucket == placement bucket, and pruning still narrows to one sub-index) rather than just "record found", which would mask a regression to unconditional -1. The COLLATE CI test deliberately using a non-power-of-two bucket count (3) to avoid the arithmetic accident that hides the defect at 8/16/32 shows real care. The out-of-int-range guards in the loops are correct.

Minor points / questions (non-blocking)

  1. Placement is not normalised. getBucketIdByRecord still hashes the raw record.get(prop) value. This is correct as long as every write funnels through convertValueToSchemaType (as the PR states). If any path can populate a declared partition property while bypassing that coercion (bulk import, direct deserialization of externally-written data), a record could be placed with a differently-boxed hash than a fresh lookup now computes, reintroducing a miss. Worth a sentence in the Javadoc noting placement correctness depends on that write-path invariant.
  2. Per-lookup cost. getBucketIdByKeys now does more work per call (index resolution in partitionKeyIsCaseInsensitive(), database resolution, per-key property lookup + Type.convert) versus the old bare hashCode(). This is the query-planning path, not per-record, and the Javadoc already justifies re-resolving rather than caching, so acceptable - just flagging it is a real (small) shift.
  3. Broad catch (Exception) in toStoredForm logs at FINE and fans out. Correct and defensively reasonable; the rationale comment is appreciated. FINE means a genuine conversion regression would be near-invisible in production - a rate-limited WARNING might surface it better, though it risks log spam on legitimately-uncoercible keys, so FINE is defensible.

Style / conventions

Consistent with the codebase - final on params/locals, single-statement if without braces, imports (no FQNs), assertThat(...).isTrue() test style, and Apache headers all present.

Overall: correct, well-tested, and conservative in exactly the way this class of bug demands. LGTM.

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 45.00000% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.92%. Comparing base (f0f341d) to head (0a58a2c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...onstrategy/PartitionedBucketSelectionStrategy.java 50.00% 7 Missing and 4 partials ⚠️
...in/java/com/arcadedb/database/MutableDocument.java 16.66% 9 Missing and 1 partial ⚠️
engine/src/main/java/com/arcadedb/schema/Type.java 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5598      +/-   ##
============================================
- Coverage     65.93%   65.92%   -0.01%     
+ Complexity     1113     1112       -1     
============================================
  Files          1751     1751              
  Lines        147814   147836      +22     
  Branches      31605    31611       +6     
============================================
+ Hits          97457    97467      +10     
- Misses        37347    37354       +7     
- Partials      13010    13015       +5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lvca lvca self-assigned this Jul 30, 2026
@lvca lvca added this to the 26.8.1 milestone Jul 30, 2026
@lvca
lvca merged commit aee7689 into main Jul 30, 2026
26 of 31 checks passed
@lvca
lvca deleted the issue-5595 branch July 30, 2026 23:08
mergify Bot added a commit that referenced this pull request Aug 5, 2026
Bumps [undici](https://github.com/nodejs/undici) from 8.5.0 to 8.10.0.
Release notes

*Sourced from [undici's releases](https://github.com/nodejs/undici/releases).*

> v8.10.0
> -------
>
> What's Changed
> --------------
>
> * feat: namespace h2 options by [`@​metcoder95`](https://github.com/metcoder95) in [nodejs/undici#5498](https://redirect.github.com/nodejs/undici/pull/5498)
> * test: update WPT expectations by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5587](https://redirect.github.com/nodejs/undici/pull/5587)
> * test: add cache/dedupe + dns re-dispatch integration tests by [`@​GiHoon1123`](https://github.com/GiHoon1123) in [nodejs/undici#5535](https://redirect.github.com/nodejs/undici/pull/5535)
> * fix(websocket): support process.unref by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5578](https://redirect.github.com/nodejs/undici/pull/5578)
> * fix(h2): ensure every request settles by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5603](https://redirect.github.com/nodejs/undici/pull/5603)
> * fix(readable): consume a body whose end has already been emitted by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5617](https://redirect.github.com/nodejs/undici/pull/5617)
> * fix(retry): skip the content-length checkpoint for HEAD and for a 206 without content-range by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5610](https://redirect.github.com/nodejs/undici/pull/5610)
> * fix: revert idle socket validation to setTimeout(0) to prevent stall on idle event loop by [`@​marceli1404`](https://github.com/marceli1404) in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606)
> * fix(env-http-proxy-agent): match bare IPv6 addresses in no\_proxy by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5623](https://redirect.github.com/nodejs/undici/pull/5623)
> * test: handle aggregate balanced pool errors by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5377](https://redirect.github.com/nodejs/undici/pull/5377)
> * fix(readable): keep body bytes that arrive after setEncoding() by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5620](https://redirect.github.com/nodejs/undici/pull/5620)
> * fix(socks5): evict unused origin pools by [`@​Kkartik14`](https://github.com/Kkartik14) in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595)
> * fix: skip deduplication for upgrade requests by [`@​Ram-blip`](https://github.com/Ram-blip) in [nodejs/undici#5593](https://redirect.github.com/nodejs/undici/pull/5593)
> * fix(retry): forward informational responses by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5625](https://redirect.github.com/nodejs/undici/pull/5625)
> * fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView reply bodies by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5619](https://redirect.github.com/nodejs/undici/pull/5619)
> * fix(interceptors): cache() and deduplicate() silently inert on Client/Pool without opts.origin by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5628](https://redirect.github.com/nodejs/undici/pull/5628)
> * build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5633](https://redirect.github.com/nodejs/undici/pull/5633)
> * build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5634](https://redirect.github.com/nodejs/undici/pull/5634)
> * build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5636](https://redirect.github.com/nodejs/undici/pull/5636)
> * fix(mock): emit request body lifecycle hooks by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5367](https://redirect.github.com/nodejs/undici/pull/5367)
> * fix(h2): detach upgrade close handler after GOAWAY by [`@​pacocartones`](https://github.com/pacocartones) in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641)
> * fix: retry refused HTTP/2 streams by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5598](https://redirect.github.com/nodejs/undici/pull/5598)
> * fix: preserve DNS origin hostname on sockets by [`@​cyphercodes`](https://github.com/cyphercodes) in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577)
>
> New Contributors
> ----------------
>
> * [`@​marceli1404`](https://github.com/marceli1404) made their first contribution in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606)
> * [`@​Kkartik14`](https://github.com/Kkartik14) made their first contribution in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595)
> * [`@​pacocartones`](https://github.com/pacocartones) made their first contribution in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641)
> * [`@​cyphercodes`](https://github.com/cyphercodes) made their first contribution in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577)
>
> **Full Changelog**: <nodejs/undici@v8.9.0...v8.10.0>
>
> v8.9.0
> ------
>
> ⚠️ Security fixes
> -----------------
>
> ### High severity
>
> * [GHSA-4cwx-7wf7-3272](GHSA-4cwx-7wf7-3272): malformed qualified `private` Cache-Control directives could cause cross-user information disclosure in shared caches or a parse-time crash. The cache parser now treats empty qualified directives conservatively and safely handles mixed qualified and unqualified directives. Fixed by [4fe5bc5f](nodejs/undici@4fe5bc5) with regression coverage in [9f09b49a](nodejs/undici@9f09b49).
>
> ### Medium severity
>
> * [GHSA-m8rv-5g2x-5cg5](GHSA-m8rv-5g2x-5cg5): a malicious `type` property on a duck-typed blob-like HTTP/1.1 request body could inject CRLF sequences into the generated `content-type` header. Undici now coerces and validates the value before adding it to the request. Fixed by [7d3cf924](nodejs/undici@7d3cf92).
> * [GHSA-jr45-8vmc-qm54](GHSA-jr45-8vmc-qm54): optional whitespace around `=` in qualified `no-cache` and `private` directives could bypass shared-cache restrictions and disclose authenticated data across users. Cache-Control parsing now normalizes these forms and applies conservative cache decisions. Fixed by [c601fff1](nodejs/undici@c601fff).
> * [GHSA-8xcm-r25x-g524](GHSA-8xcm-r25x-g524): the retry interceptor could expose a stale `Content-Length` after resuming a partial response, potentially causing downstream response desynchronization, hangs, or corruption. Undici now rejects partial responses whose `Content-Length` is inconsistent with `Content-Range`. Fixed by [e11a68ed](nodejs/undici@e11a68e), with corrected fixtures in [2b3f7493](nodejs/undici@2b3f749).
> * [GHSA-v3r7-h72x-cjcm](GHSA-v3r7-h72x-cjcm): unsanitized `domain` and `unparsed` values passed to `setCookie()` could inject cookie attributes. Undici now validates cookie domains, paths, and unparsed attributes more strictly. Fixed by [10d93fc3](nodejs/undici@10d93fc).
>
> Additional hardening
> --------------------

... (truncated)


Commits

* [`c8d80e6`](nodejs/undici@c8d80e6) Bumped v8.10.0 ([#5644](https://redirect.github.com/nodejs/undici/issues/5644))
* [`66923b4`](nodejs/undici@66923b4) fix: preserve DNS origin hostname on sockets ([#5577](https://redirect.github.com/nodejs/undici/issues/5577))
* [`3926499`](nodejs/undici@3926499) fix: retry refused HTTP/2 streams ([#5598](https://redirect.github.com/nodejs/undici/issues/5598))
* [`73d6e9e`](nodejs/undici@73d6e9e) fix(h2): detach upgrade close handler after GOAWAY ([#5641](https://redirect.github.com/nodejs/undici/issues/5641))
* [`b111adb`](nodejs/undici@b111adb) fix(mock): emit request body lifecycle hooks ([#5367](https://redirect.github.com/nodejs/undici/issues/5367))
* [`ae4a3e3`](nodejs/undici@ae4a3e3) build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 ([#5636](https://redirect.github.com/nodejs/undici/issues/5636))
* [`ec3fbf1`](nodejs/undici@ec3fbf1) build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 ([#5634](https://redirect.github.com/nodejs/undici/issues/5634))
* [`2151720`](nodejs/undici@2151720) build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 ([#5633](https://redirect.github.com/nodejs/undici/issues/5633))
* [`b96a116`](nodejs/undici@b96a116) fix(interceptors): allow interceptors without opts.origin ([#5628](https://redirect.github.com/nodejs/undici/issues/5628))
* [`a18ef2d`](nodejs/undici@a18ef2d) fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView r...
* Additional commits viewable in [compare view](nodejs/undici@v8.5.0...v8.10.0)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=undici&package-manager=npm\_and\_yarn&previous-version=8.5.0&new-version=8.10.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ArcadeData/arcadedb/network/alerts).
mergify Bot added a commit that referenced this pull request Aug 5, 2026
…p ci]

Bumps [undici](https://github.com/nodejs/undici) from 8.5.0 to 8.10.0.
Release notes

*Sourced from [undici's releases](https://github.com/nodejs/undici/releases).*

> v8.10.0
> -------
>
> What's Changed
> --------------
>
> * feat: namespace h2 options by [`@​metcoder95`](https://github.com/metcoder95) in [nodejs/undici#5498](https://redirect.github.com/nodejs/undici/pull/5498)
> * test: update WPT expectations by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5587](https://redirect.github.com/nodejs/undici/pull/5587)
> * test: add cache/dedupe + dns re-dispatch integration tests by [`@​GiHoon1123`](https://github.com/GiHoon1123) in [nodejs/undici#5535](https://redirect.github.com/nodejs/undici/pull/5535)
> * fix(websocket): support process.unref by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5578](https://redirect.github.com/nodejs/undici/pull/5578)
> * fix(h2): ensure every request settles by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5603](https://redirect.github.com/nodejs/undici/pull/5603)
> * fix(readable): consume a body whose end has already been emitted by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5617](https://redirect.github.com/nodejs/undici/pull/5617)
> * fix(retry): skip the content-length checkpoint for HEAD and for a 206 without content-range by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5610](https://redirect.github.com/nodejs/undici/pull/5610)
> * fix: revert idle socket validation to setTimeout(0) to prevent stall on idle event loop by [`@​marceli1404`](https://github.com/marceli1404) in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606)
> * fix(env-http-proxy-agent): match bare IPv6 addresses in no\_proxy by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5623](https://redirect.github.com/nodejs/undici/pull/5623)
> * test: handle aggregate balanced pool errors by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5377](https://redirect.github.com/nodejs/undici/pull/5377)
> * fix(readable): keep body bytes that arrive after setEncoding() by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5620](https://redirect.github.com/nodejs/undici/pull/5620)
> * fix(socks5): evict unused origin pools by [`@​Kkartik14`](https://github.com/Kkartik14) in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595)
> * fix: skip deduplication for upgrade requests by [`@​Ram-blip`](https://github.com/Ram-blip) in [nodejs/undici#5593](https://redirect.github.com/nodejs/undici/pull/5593)
> * fix(retry): forward informational responses by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5625](https://redirect.github.com/nodejs/undici/pull/5625)
> * fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView reply bodies by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5619](https://redirect.github.com/nodejs/undici/pull/5619)
> * fix(interceptors): cache() and deduplicate() silently inert on Client/Pool without opts.origin by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5628](https://redirect.github.com/nodejs/undici/pull/5628)
> * build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5633](https://redirect.github.com/nodejs/undici/pull/5633)
> * build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5634](https://redirect.github.com/nodejs/undici/pull/5634)
> * build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 by [`@​dependabot`](https://github.com/dependabot)[bot] in [nodejs/undici#5636](https://redirect.github.com/nodejs/undici/pull/5636)
> * fix(mock): emit request body lifecycle hooks by [`@​marko1olo`](https://github.com/marko1olo) in [nodejs/undici#5367](https://redirect.github.com/nodejs/undici/pull/5367)
> * fix(h2): detach upgrade close handler after GOAWAY by [`@​pacocartones`](https://github.com/pacocartones) in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641)
> * fix: retry refused HTTP/2 streams by [`@​mcollina`](https://github.com/mcollina) in [nodejs/undici#5598](https://redirect.github.com/nodejs/undici/pull/5598)
> * fix: preserve DNS origin hostname on sockets by [`@​cyphercodes`](https://github.com/cyphercodes) in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577)
>
> New Contributors
> ----------------
>
> * [`@​marceli1404`](https://github.com/marceli1404) made their first contribution in [nodejs/undici#5606](https://redirect.github.com/nodejs/undici/pull/5606)
> * [`@​Kkartik14`](https://github.com/Kkartik14) made their first contribution in [nodejs/undici#5595](https://redirect.github.com/nodejs/undici/pull/5595)
> * [`@​pacocartones`](https://github.com/pacocartones) made their first contribution in [nodejs/undici#5641](https://redirect.github.com/nodejs/undici/pull/5641)
> * [`@​cyphercodes`](https://github.com/cyphercodes) made their first contribution in [nodejs/undici#5577](https://redirect.github.com/nodejs/undici/pull/5577)
>
> **Full Changelog**: <nodejs/undici@v8.9.0...v8.10.0>
>
> v8.9.0
> ------
>
> ⚠️ Security fixes
> -----------------
>
> ### High severity
>
> * [GHSA-4cwx-7wf7-3272](GHSA-4cwx-7wf7-3272): malformed qualified `private` Cache-Control directives could cause cross-user information disclosure in shared caches or a parse-time crash. The cache parser now treats empty qualified directives conservatively and safely handles mixed qualified and unqualified directives. Fixed by [4fe5bc5f](nodejs/undici@4fe5bc5) with regression coverage in [9f09b49a](nodejs/undici@9f09b49).
>
> ### Medium severity
>
> * [GHSA-m8rv-5g2x-5cg5](GHSA-m8rv-5g2x-5cg5): a malicious `type` property on a duck-typed blob-like HTTP/1.1 request body could inject CRLF sequences into the generated `content-type` header. Undici now coerces and validates the value before adding it to the request. Fixed by [7d3cf924](nodejs/undici@7d3cf92).
> * [GHSA-jr45-8vmc-qm54](GHSA-jr45-8vmc-qm54): optional whitespace around `=` in qualified `no-cache` and `private` directives could bypass shared-cache restrictions and disclose authenticated data across users. Cache-Control parsing now normalizes these forms and applies conservative cache decisions. Fixed by [c601fff1](nodejs/undici@c601fff).
> * [GHSA-8xcm-r25x-g524](GHSA-8xcm-r25x-g524): the retry interceptor could expose a stale `Content-Length` after resuming a partial response, potentially causing downstream response desynchronization, hangs, or corruption. Undici now rejects partial responses whose `Content-Length` is inconsistent with `Content-Range`. Fixed by [e11a68ed](nodejs/undici@e11a68e), with corrected fixtures in [2b3f7493](nodejs/undici@2b3f749).
> * [GHSA-v3r7-h72x-cjcm](GHSA-v3r7-h72x-cjcm): unsanitized `domain` and `unparsed` values passed to `setCookie()` could inject cookie attributes. Undici now validates cookie domains, paths, and unparsed attributes more strictly. Fixed by [10d93fc3](nodejs/undici@10d93fc).
>
> Additional hardening
> --------------------

... (truncated)


Commits

* [`c8d80e6`](nodejs/undici@c8d80e6) Bumped v8.10.0 ([#5644](https://redirect.github.com/nodejs/undici/issues/5644))
* [`66923b4`](nodejs/undici@66923b4) fix: preserve DNS origin hostname on sockets ([#5577](https://redirect.github.com/nodejs/undici/issues/5577))
* [`3926499`](nodejs/undici@3926499) fix: retry refused HTTP/2 streams ([#5598](https://redirect.github.com/nodejs/undici/issues/5598))
* [`73d6e9e`](nodejs/undici@73d6e9e) fix(h2): detach upgrade close handler after GOAWAY ([#5641](https://redirect.github.com/nodejs/undici/issues/5641))
* [`b111adb`](nodejs/undici@b111adb) fix(mock): emit request body lifecycle hooks ([#5367](https://redirect.github.com/nodejs/undici/issues/5367))
* [`ae4a3e3`](nodejs/undici@ae4a3e3) build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 ([#5636](https://redirect.github.com/nodejs/undici/issues/5636))
* [`ec3fbf1`](nodejs/undici@ec3fbf1) build(deps): bump github/codeql-action/init from 4.36.2 to 4.37.3 ([#5634](https://redirect.github.com/nodejs/undici/issues/5634))
* [`2151720`](nodejs/undici@2151720) build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 ([#5633](https://redirect.github.com/nodejs/undici/issues/5633))
* [`b96a116`](nodejs/undici@b96a116) fix(interceptors): allow interceptors without opts.origin ([#5628](https://redirect.github.com/nodejs/undici/issues/5628))
* [`a18ef2d`](nodejs/undici@a18ef2d) fix(mock): non-string path matchers under ignoreTrailingSlash, and DataView r...
* Additional commits viewable in [compare view](nodejs/undici@v8.5.0...v8.10.0)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=undici&package-manager=npm\_and\_yarn&previous-version=8.5.0&new-version=8.10.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
  
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot show  ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/ArcadeData/arcadedb/network/alerts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Partitioned type: a lookup key of a different boxed type than the stored value hashes to the wrong bucket and misses the record

1 participant