Repository navigation
Conversation
Keep lifecycle entries reserved for holders, waiters, timeout callers, and exception paths until the exact generation is unused. Revalidate decorator warm paths and keep fixed-size cache bookkeeping consistent across aliases, rollback, and reload operations.
|
Hi there, |
|
Hi there |
|
Hi there, Thanks for the contribution. I've taken a look and looks like the problem you pointed is existing and the resolution may be correct - however, this is quite fundamental piece of the server which I would prefer not to introduce big changes for two reasons:
That's not ideal state - I know. When I have a moment in the following weeks I will come back and reflect on the decision, but we are not merging it to todays release. |
|
Thanks for explaining the server replacement work. I’ll keep this PR at its current scope rather than expand the existing manager. The deterministic lock-generation and cache-rollback cases may still be useful as regression requirements for the replacement, even if this implementation is not adopted. Is there a public component or issue in that effort where my concurrency and testing work would be useful? I’d like to align the next contribution with the direction the team is taking. |
|
@voropaevv - sure - told this in couple of places already - but I wish to build better coms channel with contributors to coordinate actions. Drop me an e-mail: [email protected] Regarding this PR - re-evaluating now to figure what we can do with it to both de-risk and solve problems |
|
Took some consideration - this looks promising as a solution for the problem. To push it further, we would need to examine this closer under load tests in different scenarios before merging. Happy to do it as other priorities allow us. In the meantime - here you can take a look our work around new server: #2251 |
|
Thanks for updating the branch, Paweł. I checked My local pytest rerun stopped before test collection because the environment Could you approve the pending UNIT TESTS - inference run For the subsequent load tests, I suggest comparing candidate and baseline |
|
Thanks for the discussion, Paweł. I completed the bounded real-server validation of Each revision ran 90 minutes on ARM64 CPU and RTX 4090: four small ONNX models, cache size two, two closed-loop clients, cached artifacts and no request retries. BASE ran before the PR on each host. All four soaks completed with sustained model churn.
All 1,030 soak errors and 15 additional calibration errors have the response size and SHA-256 matching the The original CPU retention comparison remains inconclusive because the resident sets differed. A separate short CPU pair used fresh processes, 10 minutes of churn, then explicit clear/reload to the same two models and five minutes of idle. First-to-last idle-minute RSS medians changed by +4 KiB on BASE and +196 KiB on the PR. This is a post-clear observation, not replacement evidence for retention after the original 90-minute soak, and it does not establish leak freedom. That short churn produced a separate 48/58 HTTP 503s on BASE/PR. The recorded candidate manager/cache suite completed with 311 passing tests. The evidence includes the pinned revisions, environment and model manifests, test harness, request/resource logs, restored rotated server logs and replayable analysis. Production code was not changed for these runs. The evidence is split into three ZIPs: report/reproduction, CPU evidence and GPU evidence. Extract all three into one directory, enter For the next review, do these residual roboflow-2821-cpu-evidence.zip |
PawelPeczek-Roboflow
left a comment
There was a problem hiding this comment.
We double-reviewed with @dkosowski87
looks good - approving now, next week we will merge, load-test internally and ship.
[ON HOLD FOR NEXT RELEASE]
|
Hi Paweł, a quick follow-up on this PR. The pending CI runs have now expired while awaiting approval; the Would you prefer me to update the branch from main before another CI Thanks! |
|
Thanks, Paweł. I updated the branch to The conflict resolution keeps the lifecycle-lock semantics and uses the current alias/capability/output registration key consistently for the warm path. The diff against that main remains confined to the five PR files. Local validation on this head: 355 manager/cache tests passed, and The earlier 90-minute CPU/GPU results still refer to Could you approve any CI runs that require maintainer authorization for the updated head? |
Why
ModelManagercould remove a per-model lock entry while the same lockgeneration was still held or referenced by waiting callers.
A subsequent
add_model()for the same resolved model identifier could thencreate a new generation. A caller waiting on the old lock and a newcomer using
the new lock could initialize the same model concurrently.
The same mutation lifecycle exposed related consistency problems in manager
decorators:
remove/add order;
underlying manager but absent from the LRU queue;
did not complete;
failed eviction candidate from the queue.
Fixes #2819.
What changed
entry.
decision under the stable per-model lifecycle lock.
and cache bookkeeping.
add/remove attempts.
original relative order.
eviction failure.
resolved identifier.
synchronization points rather than scheduling sleeps.
Public method signatures and configured per-model lock-acquisition timeout
values are unchanged. The shared lifecycle-entry guard is not held while
waiting for a per-model lock, constructing a model, or clearing model
resources.
This change addresses the demonstrated process-local load/remove/reload races
and fixed-size cache bookkeeping inconsistencies within one
ModelManagerinstance. It does not introduce active inference-use leases or cross-process
coordination.
Verification
The parent behavior was verified from:
aa9306bd8e3c674c3c0a324635307b7272eb54efParent regression proof
The original lock-generation regression failed on the unchanged parent:
It observed two live lock generations and two concurrent constructors for the
same resolved model identifier.
The expanded mutation regression selection produced:
It reproduced:
The additional exact-order rollback tests all failed on the unchanged parent:
The tests cover:
test_fixed_size_cache_failed_direct_remove_preserves_exact_lru_ordertest_fixed_size_cache_failed_eviction_restores_skipped_pinned_ordertest_fixed_size_cache_failed_multi_eviction_keeps_only_registered_modelsCandidate
The expanded mutation regression selection passed:
The additional exact-order rollback selection passed:
Targeted manager tests:
Manager and cache suites:
Full applicable CPU unit suite with a fresh model cache:
The same three failures reproduce on the unchanged parent:
test_factory_logs_construction_failures_before_falling_backtest_cached_model_aggregation_excludes_conflicting_metadatatest_index_list_parameters_by_frame_id_warns_once_per_keyThey are existing logging-capture failures where the warning is emitted but is
not available through
caplog.Incomplete implementations rejected
The regression coverage rejects intentionally incomplete alternatives:
Quality gates
Passed:
git diff --checkThe informational Flake8 pass still reports the existing upstream warning set.
Not verified
non-mutation access paths.
max_sizeguarantee during simultaneous cold loads ofdifferent identifiers.
Risks / rollback
The lifecycle-entry guard briefly serializes entry reservation and retirement,
but it is never held during per-model lock waiting, model construction, or
resource cleanup.
Post-mutation queue reconciliation uses a blocking
_queue_lockacquisition soa secondary bookkeeping timeout cannot replace the original add/remove result
or exception. It can therefore wait behind an ongoing eviction.
The existing eviction path already holds
_queue_lockwhile removing anevicted model and running
clear_cache(). This patch does not acquire_queue_lockwhile holding a per-model lifecycle lock, so it does notintroduce the opposite lock order. Reducing the existing eviction critical
section is outside this change.
Different resolved model identifiers continue to use independent per-model
locks.
Reverting this PR restores the previous mutation and cache-bookkeeping
behavior.
Migrations / external effects
No migrations or public API changes.
A warm
add_model()call that overlaps an already-started removal may now waitfor that removal to complete instead of returning from an unlocked decorator
fast path. Configured per-lock acquisition timeout values are unchanged, although this path may perform two sequential lock acquisitions.
Screenshots / preview
Not applicable.