fix!: do not count downtime towards crawler_runtime after migration or resurrection - #2204
Draft
Pijukatel wants to merge 4 commits into
Draft
fix!: do not count downtime towards crawler_runtime after migration or resurrection#2204Pijukatel wants to merge 4 commits into
Pijukatel wants to merge 4 commits into
Conversation
… resurrection When a crawler run was resumed from a persisted state, two issues inflated the reported `crawler_runtime`: - `Statistics.__aenter__` started the periodic logger before updating `crawler_last_started_at`, so the first statistics log of the resumed run computed the runtime from the previous run's start time and included all the downtime between the runs (later stats were computed correctly, which made the value appear to "jump back"). - When the state came from a run that did not finish cleanly (migration, abort), `StatisticsState.model_post_init` computed the accumulated runtime offset using the current time, permanently baking the downtime between the runs into all subsequent statistics. The offset is now computed from the moment the state was last persisted instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMHwDx87jV4rnu5LkbpNxy
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2204 +/- ##
==========================================
+ Coverage 93.74% 93.75% +0.01%
==========================================
Files 181 181
Lines 12852 12862 +10
==========================================
+ Hits 12048 12059 +11
+ Misses 804 803 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The first periodic statistics log must report exactly the runtime accumulated by previous runs: zero for a fresh start, the previous total for a resumed run. `Statistics.__aenter__` now resets the run timestamps before starting the periodic logger and sets the new start time after the initial log entry is printed. The accumulated runtime is now also restored from the persisted `crawlerRuntimeMillis` value instead of being reconstructed from the last run's timestamps, so runs before the most recent one are no longer lost from the total, and a state persisted mid-run keeps the correct runtime. The timestamp-based reconstruction is kept as a fallback for states persisted by older versions. `crawlerRuntimeMillis` is also serialized as milliseconds now, matching its name and the other `*Millis` fields, instead of an ISO 8601 duration string. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMHwDx87jV4rnu5LkbpNxy
… on Python 3.10 `datetime.fromisoformat` does not accept the 'Z' suffix until Python 3.11, which failed the resumed-run test on the 3.10 CI jobs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMHwDx87jV4rnu5LkbpNxy
Collaborator
Author
|
Waiting for V2 as this might be breaking change and will inlcude removal of deprecated fields |
Remove statistics API that is deprecated or was never populated, and make loading of persisted states tolerant of invalid runtime values: - Remove the deprecated `StatisticsState.crawler_runtime` setter; the property is read-only now. - Remove `StatisticsState.crawler_runtime_for_serialization`; the `runtime_offset` field serializes the live runtime under `crawlerRuntimeMillis` itself and restores it when a state is loaded. - Remove the never-populated state fields `errors`, `retry_errors`, `requests_finished_per_minute` and `requests_failed_per_minute`. The `FinalStatistics` fields of the same names are unaffected. - An invalid persisted `crawlerRuntimeMillis` value no longer fails loading the whole state: it is treated as absent and the runtime is reconstructed from the timestamps, as for states persisted by older versions. A negative persisted value is clamped to zero. Add the v2 upgrading guide describing the statistics changes. BREAKING CHANGE: `StatisticsState.crawler_runtime` is read-only, `StatisticsState.crawler_runtime_for_serialization` and the unused state fields `errors`, `retry_errors`, `requests_finished_per_minute` and `requests_failed_per_minute` were removed, and `crawlerRuntimeMillis` is serialized as a number of milliseconds instead of an ISO 8601 duration string. Closes #1567. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VMHwDx87jV4rnu5LkbpNxy
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.
Description
Fixes
crawler_runtimemiscounting when a run is resumed from a persisted state (migration, resurrection, abort), targeting the v2 release. End-user behavior is unchanged except for the runtime-counting fixes.Guarantees after this change:
crawlerRuntimeMillisvalue, which every persist writes as the live total, so nothing is lost with repeated resurrections (previously only the last run's segment survived).crawlerRuntimeMillis), the end of an uncleanly finished run is approximated bystats_persisted_at, so downtime is not baked into the total.crawlerRuntimeMillis(null, garbage) does not fail loading the state - it falls back to the timestamp reconstruction; a negative value is clamped to zero.Breaking changes (v2)
StatisticsState.crawler_runtimeis a read-only property; the deprecated setter is removed (closes Remove deprecatedStatisticsState.crawler_runtimesetter #1567).StatisticsState.crawler_runtime_for_serializationis removed; theruntime_offsetfield serializes the live runtime undercrawlerRuntimeMillisand restores it on load.crawlerRuntimeMillisis serialized as a number of milliseconds (matching the other*Millisfields and Crawlee for JavaScript) instead of an ISO 8601 duration string. States persisted by v1.x load correctly.errors,retry_errors,requests_finished_per_minuteandrequests_failed_per_minuteare removed fromStatisticsState. TheFinalStatisticsfields of the same names and the logged per-minute rates are unaffected.The changes are documented in the new
docs/upgrading/upgrading_to_v2.md.Issues
StatisticsState.crawler_runtimesetter #1567crawler_runtimeof 38.01s that later dropped to 542.8ms; no issue filed.Testing
tests/unit/_statistics/test_persistence.pycover: fresh run's first log is exactly zero; resumed run's first log equals the persisted runtime without a simulated 2-hour downtime; the total accumulates across multiple resurrections; unclean shutdowns do not bake downtime into the total; v1.x ISO-string states load; null/garbagecrawlerRuntimeMillisfalls back to timestamps; negative values are clamped.poe lint,poe type-check, and the unit test suite pass locally on Python 3.10 and 3.13 (only the pre-existing browser-environment failures unrelated to this change).Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01VMHwDx87jV4rnu5LkbpNxy