Skip to content

Enforce configurable maximum ledger transaction size - #7992

Open
Amaury Chamayou (achamayou) with Copilot wants to merge 20 commits into
mainfrom
copilot/enforce-configurable-max-transaction-size
Open

Enforce configurable maximum ledger transaction size#7992
Amaury Chamayou (achamayou) with Copilot wants to merge 20 commits into
mainfrom
copilot/enforce-configurable-max-transaction-size

Conversation

Copilot AI commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Closes #7488.

Ledger entries store their body size in a 6-byte field, with no further bound, so malformed input could trigger an out-of-bounds read or an excessive allocation. This adds a configurable write-time bound and rejects oversized transactions before they mutate the KV store.

Configuration

New ledger.max_transaction_size, defaulting to 64MB:

"ledger": {
  "chunk_size": "5MB",
  "max_transaction_size": "64MB"
}

The limit covers the complete ledger entry: the fixed 8-byte entry header, ledger encryption header, public-domain size field, public domain and encrypted private domain. Size strings now use checked integer scaling, so values that cannot fit in size_t fail deterministically during configuration parsing.

The default memory.max_msg_size is increased to 65MB, leaving sufficient ring-buffer and ledger-range response headroom for a maximum-sized transaction. Startup validation rejects custom configurations unless ledger.max_transaction_size is at least 2048 bytes smaller than memory.max_msg_size.

Reserved transactions are used solely for internal signatures and remain deliberately exempt: they must fill their pre-reserved ledger version, so rejecting one would create a permanent sequence hole. Snapshots are also exempt because they represent accumulated state rather than a single transaction.

Enforcement

Writes. Before apply_changes(), each non-reserved transaction is walked with a SizeWriter that performs no allocation or byte copying. It uses the same per-field size implementation as RawWriter, includes encryption and fixed-entry overhead, and rejects entries above the configured maximum with 413 / TransactionTooLarge while the store is unchanged.

Conflict detection then runs normally. Conflicting transactions discard only the scalar size accounting; they never materialise serialised public/private domains. After a successful apply assigns the transaction version, the write set is serialised and encrypted once with the final version and commit-evidence digest. Debug builds assert that the projected and actual entry sizes agree, while the final serialiser check remains a fatal safeguard for post-apply invariant failures.

get_header_length() is intentionally not cached. After separating sizing from serialisation it is called once by each short-lived wrapper, so caching offers no meaningful hot-path benefit and would introduce state whose validity depends on the encryptor implementation.

TransactionTooLarge is distinct from the HTTP parser's RequestBodyTooLarge, which also uses status 413 but closes the session.

Reads. LedgerEnclave::get_entry and skip_entry share bounds validation for both the fixed header and claimed body size. Malformed new or duplicate AppendEntries are rejected with std::logic_error, which Raft catches and answers with a NACK, rather than reading beyond the supplied buffer or allocating from an untrusted size.

Deserialisation, historical queries and snapshots are not subject to the configured transaction limit. Entries written under a larger or previously unset limit therefore remain readable during recovery and historical access.

Testing

  • Unit coverage for exact public and encrypted-private entry boundaries, RawWriter/SizeWriter agreement, reserved-signature and snapshot exemptions, deserialisation exemption, commit-evidence metadata, checked size-string overflow, and unrepresentable configured values.
  • Ledger parser coverage for truncated headers and claimed bodies larger than the supplied buffer on both read and skip paths.
  • E2e coverage in tests/limits.py asserts TransactionTooLarge, keeps the payload below max_http_body_size, and confirms rejection writes nothing and does not prevent later transactions.
  • The batched JS stress test now verifies that an oversized write is rejected with TransactionTooLarge, all nodes remain alive, and a subsequent transaction commits successfully.

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copilot AI changed the title [WIP] Add configurable maximum transaction size enforcement Enforce configurable maximum ledger transaction size Jun 27, 2026
…nsaction_size_limit e2e test

- clang-tidy (modernize-use-nodiscard) required get_max_transaction_size()
  to be marked [[nodiscard]], matching the convention already used for
  other const getters in AbstractStore.
- The transaction_size_limit e2e test configured ledger.max_transaction_size
  to 20KB before starting the network, which is smaller than the
  constitution scripts written to the KV store during service creation,
  causing genesis to fail. Raise the configured limit to 512KB (comfortably
  above the genesis transaction size) and the oversized test payload to 1MB
  so the 413 path is still exercised.
@achamayou

Copy link
Copy Markdown
Member

Copilot look at the CI failure during documentation build, and make the necessary fix.

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Copilot look at the CI failure during documentation build, and make the necessary fix.

Fixed in 80bad36 by documenting the new max_transaction_size parameter that Doxygen reported as missing. I also validated with sphinx-build --fail-on-warning -b html doc /tmp/ccf-doc-html.

@achamayou

Copy link
Copy Markdown
Member

Copilot resolve the merge conflicts in this pull request

…gurable-max-transaction-size

# Conflicts:
#	src/kv/generic_serialise_wrapper.h

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>

Copilot AI commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Copilot resolve the merge conflicts in this pull request

Resolved the merge conflicts in c068e5f. The PR is no longer reported as dirty; remaining blockers appear to be non-conflict checks/review state.

- Exempt snapshots from the per-transaction limit (serialise and
  deserialise) and add a regression test; snapshots capture whole-store
  state and may legitimately exceed a single-transaction cap.
- Move the CHANGELOG entry from the released 5.0.0 section to 7.0.7
  (Changed), fix 'serialized' -> 'serialised', and reference PR #7992
  instead of tracking issue #7488.
- Rename describe_serialised_entry_size_error to British spelling to
  match the surrounding code.
- Document that historical-query stores deliberately do not apply the
  limit (read-only reconstruction).
- Add a Store::set_max_transaction_size out-of-range validation test.
- Add missing trailing newline to serialised_entry_format.h.
- Exempt all deserialisation from the size cap; only serialisation of new
  transactions is capped. Remove the limit parameter and check from the
  deserialiser init, the abstract deserialiser interface, deserialise_views,
  get_entry, and both recovery loops; drop recovery_store's limit. Buffer-
  bounds safety checks (header size vs buffer, get_entry truncation) remain.
- Apply the cap to the whole serialised ledger entry (fixed 8-byte header
  plus body) instead of just the body.
- Update the error message, CHANGELOG, and host config schema description;
  remove a stale get_entry doc comment.
- Replace the deserialise-rejection unit test with a deserialise-exemption
  test.
…-size

Resolve conflicts in doc/host_config_schema/host_config.json (take main's formatting and re-add max_transaction_size) and src/kv/test/kv_serialisation.cpp (keep both sets of includes).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Enforcing the limit during serialisation meant the transaction had already taken a version and mutated the maps, so it could only be undone by calling Store::rollback. That is unsafe: apply_changes notes that other non-conflicting transactions may commit at later versions concurrently, so rolling back to the pre-apply TxID can discard their changes, and Store::rollback also clears pending_txs and bumps rollback_count.

Project the exact size of the ledger entry before apply_changes instead, by serialising the change sets (which apply_changes does not modify) through a serialiser that only measures them. An oversized transaction is then rejected without the store having been touched, so it neither writes a value nor stops later transactions.

The limit is still checked when serialising, but as a fatal KvSerialiserException, matching how other post-apply serialisation failures are treated.

Use a distinct TransactionTooLarge error code, so the rejection cannot be confused with the HTTP parser's RequestBodyTooLarge, which is also a 413 and closes the session.

Move the CHANGELOG entry to the current version, under Added.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Serialise transaction domains once before applying changes, reuse the retained bytes after assigning the transaction version, and exempt reserved signature transactions from the configurable cap.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a configurable ledger transaction-size limit to prevent oversized allocations and reject excessive writes before store mutation.

Changes:

  • Adds ledger.max_transaction_size, documentation, and TransactionTooLarge.
  • Pre-serialises transactions for size validation while exempting signatures and snapshots.
  • Adds ledger-read bounds checking and unit/e2e coverage.

Custom instructions used

  • .github/copilot-instructions.md
  • .github/instructions/changelog.instructions.md
  • .github/instructions/reviewing.instructions.md

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/limits.py Adds transaction-limit e2e coverage.
tests/infra/network.py Forwards the new test argument.
tests/infra/e2e_args.py Adds the ledger-limit CLI option.
tests/config.jinja Emits the new configuration field.
src/node/rpc/frontend.h Maps oversized transactions to HTTP 413.
src/node/historical_queries.h Documents historical-query exemption.
src/kv/test/kv_snapshot.cpp Tests snapshot exemption.
src/kv/test/kv_serialisation.cpp Tests limits, boundaries, and exemptions.
src/kv/store.h Stores and validates the configured limit.
src/kv/serialised_entry_format.h Defines representable entry limits.
src/kv/raw_serialise.h Adds size estimation and field patching.
src/kv/kv_types.h Adds the exception and store API.
src/kv/generic_serialise_wrapper.h Enforces incremental size accounting.
src/kv/committable_tx.h Pre-serialises before applying writes.
src/enclave/main.cpp Passes configuration into the enclave.
src/enclave/enclave.h Applies the limit to the store.
src/consensus/ledger_enclave.h Validates ledger entry bounds.
src/common/configuration.h Adds JSON serialisation support.
include/ccf/odata_error.h Adds TransactionTooLarge.
include/ccf/node/startup_config.h Defines the 100 MB default.
doc/host_config_schema/host_config.json Documents the configuration schema.
CHANGELOG.md Records the user-facing behavior.
Suppressed comments (1)

src/consensus/ledger_enclave.h:35

  • This validation is bypassed for entries in an already-deserialised prefix: raft.h:1308-1311 calls LedgerEnclave::skip_entry directly and outside the std::logic_error catch around get_entry. skip_entry still trusts the header and throws serialized::InsufficientSpaceException for a truncated body, so malformed duplicate AppendEntries can escape rather than being rejected with a NACK. Apply equivalent validation and exception handling to the skip path.
      // The size in the entry header is not trusted: check it against the
      // buffer we were given before allocating. This is distinct from the
      // configured max_transaction_size, which applies only when serialising
      // new transactions, so that entries written under a larger or unset
      // limit can always be read back.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/consensus/ledger_enclave.h
Comment thread src/enclave/main.cpp
Comment thread src/consensus/ledger_enclave.h Outdated
Cover exact size enforcement with a real encryption header and mixed public/private writes, and satisfy clang-tidy by initializing the serialised version offset in the constructor initializer list.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject truncated ledger headers and bodies consistently on read and skip paths, NACK malformed duplicate entries, and use checked integer scaling for size strings.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Measure transaction entries without materialising bytes before conflict detection, serialise only successful writes, align the transaction default to 64MB, and preserve ring-buffer response headroom with a 65MB message default and startup validation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@achamayou
Amaury Chamayou (achamayou) marked this pull request as ready for review August 13, 2026 17:01
@achamayou
Amaury Chamayou (achamayou) requested a review from a team as a code owner August 13, 2026 17:01
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Description

Comparing 3 available runs from this branch (#7992) against the trend of the last 30 main runs.

Each chart plots every benchmark as an axis, with values normalized so 100 is the EWMA baseline of recent main runs, using a 7-run half-life. The 3 orange branch lines run from the oldest (faintest) to the latest (darkest and thickest); the darker blue band is the main baseline +/- 1 std dev and the lighter blue band around it is +/- 2 std dev.

Axis labels show the latest branch value and its difference from the main EWMA baseline, where 0% is on the baseline. They are coloured green where the latest run improves on the baseline, red where it regresses, and grey where the difference is within one std dev of the baseline (within noise). Higher is better for throughput and rate, lower for latency and memory.

Throughput (tx/s)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(7){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Basic: 67,816 tx/s ▬ +2%"]
  axis b1["Basic Blocking: 1,022 tx/s ▬ +1%"]
  axis b2["Basic JS: 4,700 tx/s ▬ -1%"]
  axis b3["Basic Multi-Threaded: 85,478 tx/s ▬ 0%"]
  axis b4["Historical Queries: 212,382 tx/s ▬ +4%"]
  axis b5["Logging: 63,529 tx/s ▬ +3%"]
  axis b6["Logging JWT: 10,370 tx/s ▬ +3%"]
  curve stddev2_high["main EWMA + 2 std dev"]{106.27, 103.47, 106.15, 109.12, 109.30, 107.90, 107.72}
  curve stddev1_high["main EWMA + 1 std dev"]{103.14, 101.74, 103.08, 104.56, 104.65, 103.95, 103.86}
  curve stddev1_low["main EWMA - 1 std dev"]{96.86, 98.26, 96.92, 95.44, 95.35, 96.05, 96.14}
  curve stddev2_low["main EWMA - 2 std dev"]{93.73, 96.53, 93.85, 90.88, 90.70, 92.10, 92.28}
  curve branch_0["#7992 (2 runs earlier)"]{103.31, 100.81, 103.26, 100.20, 102.58, 96.37, 98.58}
  curve branch_1["#7992 (1 run earlier)"]{92.30, 98.66, 98.77, 89.41, 90.94, 102.43, 102.13}
  curve branch_2["#7992"]{101.68, 100.85, 98.84, 100.33, 103.53, 102.64, 102.96}
  graticule polygon
  max 117
  min 82
  ticks 0
  showLegend false
Loading

Latency (ms)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Commit Latency 16ms: 4.36 ms ▬ -26%"]
  axis b1["Commit Latency 1ms: 1.96 ms ▬ +4%"]
  axis b2["Commit Latency 256ms: 204 ms ▬ 0%"]
  curve stddev2_high["main EWMA + 2 std dev"]{169.45, 110.36, 101.91}
  curve stddev1_high["main EWMA + 1 std dev"]{134.72, 105.18, 100.95}
  curve stddev1_low["main EWMA - 1 std dev"]{65.28, 94.82, 99.05}
  curve stddev2_low["main EWMA - 2 std dev"]{30.55, 89.64, 98.09}
  curve branch_0["#7992 (2 runs earlier)"]{89.45, 103.14, 99.59}
  curve branch_1["#7992 (1 run earlier)"]{68.42, 105.69, 99.65}
  curve branch_2["#7992"]{74.42, 103.51, 99.61}
  graticule polygon
  max 219
  ticks 0
  showLegend false
Loading

Memory (bytes)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#2DA44E!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#E5484D!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["Basic: 82.5 MiB ▼ 5%"]
  axis b1["Basic Blocking: 71.6 MiB ▬ 0%"]
  axis b2["Basic JS: 71.3 MiB ▬ +1%"]
  axis b3["Basic Multi-Threaded: 87.9 MiB ▼ 1%"]
  axis b4["Logging: 76.2 MiB ▬ 0%"]
  axis b5["Logging JWT: 69.4 MiB ▲ 1%"]
  curve stddev2_high["main EWMA + 2 std dev"]{106.88, 100.58, 103.40, 102.09, 101.51, 101.58}
  curve stddev1_high["main EWMA + 1 std dev"]{103.44, 100.29, 101.70, 101.04, 100.75, 100.79}
  curve stddev1_low["main EWMA - 1 std dev"]{96.56, 99.71, 98.30, 98.96, 99.25, 99.21}
  curve stddev2_low["main EWMA - 2 std dev"]{93.12, 99.42, 96.60, 97.91, 98.49, 98.42}
  curve branch_0["#7992 (2 runs earlier)"]{99.53, 100.15, 100.23, 101.12, 100.17, 99.41}
  curve branch_1["#7992 (1 run earlier)"]{96.10, 100.09, 100.09, 103.19, 98.96, 100.59}
  curve branch_2["#7992"]{94.67, 100.05, 100.58, 98.70, 100.12, 100.87}
  graticule polygon
  max 112
  min 88
  ticks 0
  showLegend false
Loading

Rate (ops/s)

---
config:
  radar:
    width: 620
    height: 620
    marginTop: 90
    marginRight: 220
    marginBottom: 60
    marginLeft: 220
    axisLabelFactor: 1.12
    curveTension: 0.08
  theme: base
  themeCSS: |
    .radarCurve-0{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-1{fill:color-mix(in srgb, #62B5E5 40%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-2{fill:color-mix(in srgb, #62B5E5 13%, var(--color-canvas-default,var(--bgColor-default,#fff)))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarCurve-3{fill:var(--color-canvas-default,var(--bgColor-default,#fff))!important;fill-opacity:1!important;stroke:none!important;stroke-width:0!important}
    .radarAxisLabel,.radarTitle{fill:var(--color-fg-default,var(--fgColor-default,#111827))!important;color:var(--color-fg-default,var(--fgColor-default,#111827))!important}
    .radarCurve-4{stroke-width:1.5px!important;stroke-opacity:0.40!important}
    .radarCurve-5{stroke-width:1.5px!important;stroke-opacity:0.50!important}
    .radarCurve-6{stroke-width:1.75px!important;stroke-opacity:1.00!important}
    .radarAxisLabel:nth-of-type(1){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(2){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(3){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(4){fill:#E5484D!important}
    .radarAxisLabel:nth-of-type(5){fill:#808A94!important}
    .radarAxisLabel:nth-of-type(6){fill:#808A94!important}
  themeVariables:
    cScale0: "#62B5E5"
    cScale1: "#62B5E5"
    cScale2: "#62B5E5"
    cScale3: "#62B5E5"
    cScale4: "#F97316"
    cScale5: "#F97316"
    cScale6: "#F97316"
    radar:
      axisColor: "#9CA3AF"
      graticuleColor: "#E5E7EB"
      graticuleOpacity: 0
      axisStrokeWidth: 1
      curveOpacity: 0
---
radar-beta
  axis b0["CHAMP get: 37,840,435 ops/s ▬ 0%"]
  axis b1["CHAMP put: 5,456,270 ops/s ▬ 0%"]
  axis b2["KV deserialisation: 1,645,007 ops/s ▬ +2%"]
  axis b3["KV serialisation: 1,354,830 ops/s ▼ 6%"]
  axis b4["KV snapshot deserialis...: 4,186 ops/s ▬ +1%"]
  axis b5["KV snapshot serialisation: 4,668 ops/s ▬ +4%"]
  curve stddev2_high["main EWMA + 2 std dev"]{106.51, 106.12, 105.71, 104.65, 105.75, 111.77}
  curve stddev1_high["main EWMA + 1 std dev"]{103.26, 103.06, 102.86, 102.32, 102.87, 105.89}
  curve stddev1_low["main EWMA - 1 std dev"]{96.74, 96.94, 97.14, 97.68, 97.13, 94.11}
  curve stddev2_low["main EWMA - 2 std dev"]{93.49, 93.88, 94.29, 95.35, 94.25, 88.23}
  curve branch_0["#7992 (2 runs earlier)"]{100.90, 100.27, 101.06, 95.46, 103.03, 101.55}
  curve branch_1["#7992 (1 run earlier)"]{97.97, 98.12, 102.40, 93.39, 101.43, 96.89}
  curve branch_2["#7992"]{99.92, 100.12, 101.56, 93.90, 101.04, 104.40}
  graticule polygon
  max 121
  min 79
  ticks 0
  showLegend false
Loading

Replace the obsolete expectation that an oversized transaction terminates a node with assertions for TransactionTooLarge, continued node health, and a successful subsequent commit.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enforce a configurable maximum transaction size

3 participants