Implement predictive prefetcher with ML-based access pattern model#4293
Open
Implement predictive prefetcher with ML-based access pattern model#4293
Conversation
…cksDB persistence, MetricsCollector, A/B test Co-authored-by: makr-code <150588092+makr-code@users.noreply.github.com>
Owner
|
@copilot retry |
…NCEMENTS [x] for predictive prefetcher ML Co-authored-by: makr-code <150588092+makr-code@users.noreply.github.com>
Contributor
Author
Done in commit
|
Copilot
AI
changed the title
[WIP] Implement predictive prefetcher with ML-based access pattern model
Implement predictive prefetcher with ML-based access pattern model
Mar 17, 2026
There was a problem hiding this comment.
Pull request overview
Implements the v1.8.0 “Predictive Prefetcher: ML-Based Access Pattern Model” roadmap item by extending the cache prefetcher with Markov-chain-based prediction, optional time-of-day weighting, RocksDB persistence, metrics emission, an A/B toggle, and targeted CI/tests.
Changes:
- Extend
PredictivePrefetcherwith ToD weighting, persistence (save/load), metrics emission, overhead tracking, and A/B-test statistics plumbing. - Integrate new prefetcher config + persistence methods into
AdaptiveQueryCache. - Add focused unit tests and a dedicated GitHub Actions workflow for the focused target.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_predictive_prefetcher_markov.cpp | Adds focused tests for ToD, A/B toggle, metrics, persistence, and AQC forwarding. |
| tests/CMakeLists.txt | Adds a focused test target and ctest registration for Markov/ToD tests. |
| src/cache/predictive_prefetcher.cpp | Implements ToD weighting, metrics emission, RocksDB persistence, and A/B decision logic. |
| src/cache/adaptive_query_cache.cpp | Wires new config flags and adds model save/load + overhead byte forwarding; loads model in ctor. |
| src/cache/FUTURE_ENHANCEMENTS.md | Marks the four roadmap items as completed. |
| include/cache/predictive_prefetcher.h | Adds new config fields + APIs for overhead bytes and persistence; adds ToD buckets and A/B counters. |
| include/cache/adaptive_query_cache.h | Adds new config flags and new public APIs for overhead bytes + model save/load. |
| .github/workflows/predictive-prefetcher-ml-ci.yml | Adds a dedicated workflow to build/run the focused test target on multiple compilers. |
You can also share your feedback on Copilot code review. Take the survey.
src/cache/predictive_prefetcher.cpp
Outdated
Comment on lines
+46
to
+54
| bool PredictivePrefetcher::useMarkovModel(const std::string& tenant_id) const { | ||
| if (!config_.enable_ab_test) { | ||
| // No A/B test: always use time-of-day weighting when enabled. | ||
| return config_.enable_time_of_day_weighting; | ||
| } | ||
| // A/B split: deterministic hash of tenant_id. | ||
| // Group 0 → Markov model; group 1 → frequency baseline. | ||
| return (std::hash<std::string>{}(tenant_id) % 2) == 0; | ||
| } |
src/cache/predictive_prefetcher.cpp
Outdated
Comment on lines
+155
to
+156
| const auto& successors = it->second; | ||
| const bool use_tod = useMarkovModel(tenant_id); |
src/cache/predictive_prefetcher.cpp
Outdated
Comment on lines
+51
to
+53
| // A/B split: deterministic hash of tenant_id. | ||
| // Group 0 → Markov model; group 1 → frequency baseline. | ||
| return (std::hash<std::string>{}(tenant_id) % 2) == 0; |
src/cache/predictive_prefetcher.cpp
Outdated
Comment on lines
+213
to
+218
| // Update A/B generation counters | ||
| if (config_.enable_ab_test) { | ||
| if (use_tod) { | ||
| const_cast<PredictivePrefetcher*>(this)->ab_markov_generated_++; | ||
| } else { | ||
| const_cast<PredictivePrefetcher*>(this)->ab_baseline_generated_++; |
src/cache/predictive_prefetcher.cpp
Outdated
Comment on lines
225
to
229
| void PredictivePrefetcher::recordPrefetchHit() { | ||
| std::lock_guard<std::mutex> lock(mutex_); | ||
| prefetch_hits_++; | ||
| emitMetrics(); | ||
| } |
| EXPECT_EQ(candidates[0], "fp_b"); | ||
| } | ||
|
|
||
| TEST_F(ToDBucketingTest, StatsContainTimoOfDayFlag) { |
Comment on lines
+225
to
+229
| db_path_ = "/tmp/themis_test_prefetch_persist_" + | ||
| std::to_string(std::chrono::system_clock::now() | ||
| .time_since_epoch().count()) + | ||
| "_" + | ||
| std::to_string(reinterpret_cast<uintptr_t>(this)); |
Comment on lines
+320
to
+322
| const std::string db_path = "/tmp/themis_test_aqc_tod_" + | ||
| std::to_string(std::chrono::system_clock::now() | ||
| .time_since_epoch().count()); |
Comment on lines
+349
to
+351
| const std::string db_path = "/tmp/themis_test_aqc_ab_" + | ||
| std::to_string(std::chrono::system_clock::now() | ||
| .time_since_epoch().count()); |
Comment on lines
+176
to
+200
| class PrefetchMetricsTest : public ::testing::Test { | ||
| protected: | ||
| void SetUp() override { | ||
| MetricsCollector::getInstance().reset(); | ||
| } | ||
| }; | ||
|
|
||
| TEST_F(PrefetchMetricsTest, HitRateGaugeUpdatedOnPrefetchHit) { | ||
| PredictivePrefetcher::Config cfg; | ||
| cfg.max_tracked_keys = 100; | ||
| cfg.max_successors_per_key = 10; | ||
| cfg.min_transition_count = 2; | ||
| cfg.max_predictions = 3; | ||
| cfg.enable_time_of_day_weighting = false; | ||
|
|
||
| PredictivePrefetcher pf(cfg); | ||
|
|
||
| // Generate some candidates and record hits. | ||
| pf.recordCandidatesGenerated(); | ||
| pf.recordCandidatesGenerated(); | ||
| pf.recordPrefetchHit(); // This should emit metrics. | ||
|
|
||
| auto stats = pf.getStats(); | ||
| EXPECT_NEAR(stats["hit_rate"].get<double>(), 0.5, 0.01); | ||
| } |
Owner
|
@copilot Review Kommentare umsetzten |
makr-code
approved these changes
Mar 17, 2026
Co-authored-by: makr-code <150588092+makr-code@users.noreply.github.com>
Copilot stopped work on behalf of
makr-code due to an error
March 17, 2026 06:36
Owner
|
@copilot retry |
makr-code
approved these changes
Mar 17, 2026
makr-code
approved these changes
Mar 17, 2026
makr-code
approved these changes
Mar 17, 2026
makr-code
approved these changes
Mar 17, 2026
Owner
|
@copilot retry |
- Rename useMarkovModel() -> useToDWeighting(): clearly separates the concern of ToD weighting from Markov-vs-baseline; Markov transitions are always used while ToD is gated independently. - Replace std::hash<std::string> with FNV-1a 64-bit hash for stable, cross-platform A/B tenant routing (fnv1aHash() static helper). - A/B counters were already mutable; remove const_cast in getPrefetchCandidates(). Increment counters by result.size() (not 1). - Add tenant_id to recordPrefetchHit() so A/B hit counters (ab_markov_hits_/ab_baseline_hits_) are correctly incremented. - Add count+tenant_id to recordCandidatesGenerated(); comment explains per-group generated counts are handled in getPrefetchCandidates(). - Call emitMetrics() from recordCandidatesGenerated() to keep cache.prefetch.hit_rate gauge fresh in low-hit regimes. - saveModel(): batch-delete all stale prefetch_model:: prefix keys before writing the new snapshot to prevent unbounded growth. - loadModel(): compute delta (new_val - old_val) when merging via max() to prevent inflating total_transitions_recorded_ on repeated loads. Use find() instead of count()+[] to reduce map lookups. - Fix test typo: StatsContainTimoOfDayFlag -> StatsContainTimeOfDayFlag - Replace all hardcoded /tmp/ paths in tests with std::filesystem::temp_directory_path() for portability. - Add MetricsCollector gauge assertions in HitRateGaugeUpdatedOnPrefetchHit and OverheadBytesEmitted tests. Co-authored-by: makr-code <150588092+makr-code@users.noreply.github.com>
Contributor
Author
Done in commit
|
makr-code
approved these changes
Mar 17, 2026
makr-code
approved these changes
Mar 17, 2026
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
Implements the v1.8.0 roadmap item "Predictive Prefetcher: ML-Based Access Pattern Model" for
src/cache/. Replaces the simple frequency counter with a first-order Markov chain transition matrix, adds time-of-day (ToD) bucketing, RocksDB persistence, MetricsCollector emission, and an A/B test toggle for comparing Markov+ToD vs. raw Markov frequency routing.Core implementation (
src/cache/predictive_prefetcher.cpp/.h)max_tracked_keys.useToDWeighting()(renamed fromuseMarkovModel()): Markov transitions are always applied; ToD weighting is gated independently. A/B group 0 (FNV-1a hash % 2 == 0) uses Markov + ToD; group 1 uses raw Markov frequency without ToD.fnv1aHash()): Replacedstd::hash<std::string>with a portable 64-bit FNV-1a implementation for deterministic, cross-platform A/B tenant routing.const_castremoved (counters aremutable); generated counters increment byresult.size();recordPrefetchHit(tenant_id)now attributes hits to the correct A/B group counter.recordCandidatesGenerated(count, tenant_id): Accepts candidate count and tenant; callsemitMetrics()socache.prefetch.hit_rategauge stays current in low-hit regimes.saveModel(): Batch-deletes all staleprefetch_model::prefix keys before writing the new snapshot, preventing unbounded RocksDB growth.loadModel(): Computestotal_transitions_recorded_delta(new_val − old_val)when merging viamax()to prevent counter inflation on repeated loads; usesfind()instead ofcount()+[]to avoid double map lookups.cache.prefetch.hit_rateandcache.prefetch.overhead_bytesgauges on every hit, generated-candidates event, and overhead recording.AdaptiveQueryCacheintegrationprefetch_enable_time_of_day_weightingandprefetch_enable_ab_testwired intoPredictivePrefetcher::Config.getPrefetchCandidates()forwardscandidates.size()andtenant_idtorecordCandidatesGenerated().savePrefetchModel()/loadPrefetchModel()public methods; model is loaded automatically at construction whenl3_db_pathis set.recordPrefetchOverheadBytes()forwarded toPredictivePrefetcher::recordOverheadBytes().Tests (
tests/test_predictive_prefetcher_markov.cpp)ToDBucketingTest,ABTestToggleTest,PrefetchMetricsTest,PrefetchPersistenceTest, andAdaptiveQueryCachePrefetchConfigTestsuites.StatsContainTimoOfDayFlag→StatsContainTimeOfDayFlag./tmp/paths withstd::filesystem::temp_directory_path()for portability.MetricsCollector::getInstance().getPrometheusMetrics()gauge assertions toHitRateGaugeUpdatedOnPrefetchHitandOverheadBytesEmitted.Infrastructure
test_predictive_prefetcher_markov_focusedadded totests/CMakeLists.txt..github/workflows/predictive-prefetcher-ml-ci.ymlrunsPredictivePrefetcherMarkovTestson gcc-12, clang-15, and gcc-13.[x]insrc/cache/FUTURE_ENHANCEMENTS.md.Type of Change
Testing
📚 Research & Knowledge (wenn applicable)
/docs/research/angelegt?/docs/research/implementation_influence/eingetragen?Relevante Quellen:
Checklist
Original prompt
💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.