Repository navigation
[FIX] Heartbeat ceiling 600s -> 240s : it sat above the server's 300s session window and under-billed peak CCU - #3
Merged
Conversation
…n window, so it under-billed peak CCU max_heartbeat_interval_s was 600. The server's SESSION_WINDOW_MS is 300s, and Unity and Godot both clamp to 240. WHY THAT IS A BILLING BUG, NOT A CADENCE PREFERENCE. computePeakConcurrency merges consecutive beats into ONE live span only while the gap stays <= the session window. At 600s a player online continuously for an hour produces six isolated instants instead of one span. Concurrent players' spans stop overlapping, the monthly PEAK CCU comes out low, and the studio is invoiced on the low figure. An Unreal or dedicated-server integrator raising the interval to cut telemetry cost - explicitly permitted by the header's own comment - silently reduced their own bill and the product's revenue together. This is bit-for-bit the defect the Unity SDK shipped in 0.19.3 (MAX_HEARTBEAT_INTERVAL_SECONDS = 600f). Tombstack carries tests/heartbeat-cadence.test.ts to stop it recurring, and its docblock names this exact failure - but it read the C# source and ONLY the C# source, so this SDK kept 600 with a green suite on both sides. That guard now parses the ceiling out of this repo's public header as well, and asserts Unity, Godot and native agree on one number. Changed here: the enforcing std::clamp in src/client.cpp and the range documented on options.heartbeat_interval_s in the public header. A caller that passes more than 240 now gets 240; the default (60s) and floor (15s) are untouched, and no signature or ABI changes. NOT TESTED IN THIS REPO, stated plainly rather than implied. max_heartbeat_interval_s is translation-unit local and Client exposes no accessor for the resolved interval, so asserting it would mean widening internals for the test - a larger change than the fix. The contract an integrator reads IS the header, and that is now pinned from Tombstack. A follow-up that exposes the resolved interval would let tests/ cover it directly. FOLLOW-UP FLAGGED, NOT SILENTLY CARRIED: the version is duplicated as a literal in src/tombstone_api.cpp (`sdk_version`) and in project(... VERSION ...). That duplication has already shipped two wrong versions - the changelog records this string pinned at "0.1.0" once and stuck at "0.8.0" once. Both are bumped to 0.9.2 here, and both now carry a comment saying so; deriving it from PROJECT_VERSION is the real fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion to AnkleBreaker-Studio/tombstack#3, which fixed the Tombstack side of the same defect.
The bug
max_heartbeat_interval_swas 600. The server'sSESSION_WINDOW_MSis 300s, and Unity and Godot both clamp to 240.computePeakConcurrencymerges consecutive beats into one live span only while the gap stays under the session window. At 600s a player online continuously for an hour produces six isolated instants instead of one span — concurrent players stop overlapping, monthly peak CCU reads low, and the studio is invoiced on the low figure.An Unreal or dedicated-server integrator raising the interval to cut telemetry cost — explicitly permitted by the header's own comment — silently reduced their own bill and the product's revenue together.
This is bit-for-bit the defect the Unity SDK shipped in 0.19.3. Tombstack's
tests/heartbeat-cadence.test.tsexists to stop it recurring and its docblock names this exact failure, but it read the C# source only, so this SDK kept 600 with a green suite on both sides.Changed
src/client.cpp— the enforcingstd::clampceiling, 600 to 240include/tombstone/tombstone.h— the documented range onoptions.heartbeat_interval_sCMakeLists.txt+src/tombstone_api.cpp) and a CHANGELOG entryA caller passing more than 240 now gets 240. The default (60s) and floor (15s) are untouched. No signature or ABI change.
Not tested in this repo — stated plainly
max_heartbeat_interval_sis translation-unit local andClientexposes no accessor for the resolved interval, so asserting it would mean widening internals for the test — a larger change than the fix.The contract an integrator actually reads is the header, and that is now pinned from Tombstack: the guard there parses the ceiling out of this repo's public header and asserts Unity, Godot and native agree on one number. It was proved non-blind by reverting the header to 600 and watching it fail.
A follow-up exposing the resolved interval would let
tests/cover the clamp directly.Follow-up flagged, not silently carried
The version is duplicated as a literal in
src/tombstone_api.cpp(sdk_version) and inproject(... VERSION ...). That duplication has already shipped two wrong versions — the changelog records this string pinned at0.1.0once and stuck at0.8.0once. Both are bumped here and both now carry a comment saying so; deriving it fromPROJECT_VERSIONis the real fix.Test plan
cmake --build build && ctest --test-dir buildon a machine with a toolchain (not run here — no C++ toolchain in this environment)