Skip to content

Feature/product datastore - #643

Open
F1r3w477 wants to merge 6 commits into
dpaulat:developfrom
F1r3w477:feature/product-datastore
Open

Feature/product datastore#643
F1r3w477 wants to merge 6 commits into
dpaulat:developfrom
F1r3w477:feature/product-datastore

Conversation

@F1r3w477

@F1r3w477 F1r3w477 commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Slice B of the multi-provider refactor (#506): extract ProductDatastore from RadarProductManager.

Record storage, cache/LRU eviction, product-time population, and cache lookup now live in a dedicated class. RadarProductManager keeps orchestration — providers, refresh, coordinate generation, and the L2/L3 load paths — and delegates storage to productDatastore_.

Stacked on #642 (feature/extract-provider-manager / Slice A).

What moved

  • New: ProductDatastore — L2/L3 record maps, recent-record LRU, Store, GetCachedNexradFile, FindLevel2RecordEntries / FindLevel3RecordEntry, PopulateLevel2/3ProductTimes, AreProductTimesPopulated
  • Removed from RPM: ~425 lines of inline map/mutex/cache logic (StoreRadarProductRecord, UpdateRecentRecords, local typedefs)
  • Hardening:
    • Null guards on provider_ before provider calls
    • GetCachedNexradFile keys aligned to seconds (matches Store)
    • Atomic cacheLimit_ for concurrent SetCacheLimit
    • AreLevel2ProductTimesPopulated checks archive and chunks providers

Test plan

  • ProductDatastore.* unit tests (7) — dedup, find, cache lookup, sub-second cache key, cache limit floor
  • Existing RadarProductManager / ProviderManager tests pass
  • Manual: live L2 + L3, archive timeline loop, site switch, clean quit
  • Make sure CI passes

Notes

  • No user-facing behavior change intended — mechanical extract + small hardening

Move record maps, cache/LRU, populate-times, and lookup into
ProductDatastore. Harden null-provider guards, second-aligned cache
lookup, atomic cache limit, and L2 chunks time-populated check.

Refs dpaulat#506

@github-actions github-actions Bot 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.

clang-tidy made some suggestions

Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/provider_manager.hpp
Comment thread scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp Outdated
Comment thread test/source/scwx/qt/manager/radar_product_manager.test.cpp Outdated
Comment thread test/source/scwx/qt/manager/radar_product_manager.test.cpp Outdated
Comment thread test/source/scwx/qt/manager/radar_product_manager.test.cpp Outdated
Const-correct locks, deleted ProviderManager moves, explicit lambda
captures, named cache limit constant, and const ref Store params.
@dpaulat
dpaulat requested a review from Copilot May 24, 2026 03:50

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

Note

Copilot was unable to run its full agentic suite in this review.

Extracts product record storage/cache responsibilities from RadarProductManager into a dedicated ProductDatastore as part of the multi-provider refactor (Slice B), and moves the internal ProviderManager into its own compilation unit.

Changes:

  • Added ProductDatastore for record storage, cache/LRU behavior, and product-time population/lookup.
  • Extracted ProviderManager from radar_product_manager.cpp into standalone provider_manager.{hpp,cpp}.
  • Updated RadarProductManager to delegate storage and cache lookups to productDatastore_ and added/updated unit tests and build wiring.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/test.cmake Registers new ProductDatastore unit tests in the test build.
test/source/scwx/qt/manager/radar_product_manager.test.cpp Adds ProviderManager::name() formatting test and adjusts includes.
test/source/scwx/qt/manager/product_datastore.test.cpp New unit tests for datastore dedup, lookup, cache key behavior, and cache limit floor.
scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp Delegates storage/cache/time-population to ProductDatastore and uses extracted ProviderManager.
scwx-qt/source/scwx/qt/manager/provider_manager.hpp New standalone ProviderManager header.
scwx-qt/source/scwx/qt/manager/provider_manager.cpp New standalone ProviderManager implementation (refresh scheduling, shutdown hardening).
scwx-qt/source/scwx/qt/manager/product_datastore.hpp New datastore public API for caching/record maps/LRU and time population.
scwx-qt/source/scwx/qt/manager/product_datastore.cpp Implements record storage/dedup, cache lookups, time population, and iteration helpers.
scwx-qt/scwx-qt.cmake Adds new manager source/header files to the Qt build.

Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp
Comment thread scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp
Comment thread scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/product_datastore.cpp Outdated
Comment thread scwx-qt/source/scwx/qt/manager/provider_manager.cpp
F1r3w477 added 3 commits May 28, 2026 20:01
Avoid relying on transitive includes for std::max/std::find.
Align dpaulat#643 with review standards from dpaulat#642/dpaulat#644: hide implementation
details behind Impl, use ProviderManager accessors, fix RPM include
order, and add QT_NO_EMIT for wxtest on Linux.
Keep ProductDatastore delegation in RPM; integrate RadarCoordinateTable
and merged ProviderManager from develop. Drop duplicate cache/populate
logic left in develop's RPM.
@F1r3w477
F1r3w477 marked this pull request as ready for review June 24, 2026 20:38
Sync multi-provider / radar-site transition work from develop into
ProductDatastore (AreProductTimesPopulated, PopulateProductTimes) while
keeping RPM cache/load delegation through ProductDatastore.
@dpaulat

dpaulat commented Jul 17, 2026

Copy link
Copy Markdown
Owner

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added centralized caching for Level 2 and Level 3 radar products.
    • Improved retrieval of cached radar files and product records by time and product.
    • Added configurable cache limits and support for enumerating cached records and products.
  • Bug Fixes

    • Improved record deduplication and handling of expired or missing cached data.
    • Enhanced subsecond time lookups to resolve the appropriate cached radar file.
  • Tests

    • Added coverage for cache limits, record storage, time-based lookups, deduplication, and radar file retrieval.

Walkthrough

Introduces a thread-safe ProductDatastore for Level 2 and Level 3 radar product caching, provider-backed time population, record lookup, and Nexrad-file retrieval. RadarProductManager delegates caching and loading operations to it, with new GoogleTest coverage.

Changes

Product datastore integration

Layer / File(s) Summary
Datastore contract and build wiring
scwx-qt/source/scwx/qt/manager/product_datastore.hpp, scwx-qt/scwx-qt.cmake
Defines the datastore interface, record entry types, cache controls, lookup APIs, and provider population methods, then adds the implementation to the build.
Cache and product-time implementation
scwx-qt/source/scwx/qt/manager/product_datastore.cpp
Adds thread-safe Level 2/3 storage, deduplication, bounded recent records, time lookups, Nexrad-file retrieval, iteration, and parallel provider time aggregation.
RadarProductManager migration
scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp
Replaces local record maps and helper methods with datastore calls throughout loading, population, lookup, reload, dumping, and cache-limit handling.
Datastore behavior coverage
test/source/scwx/qt/manager/product_datastore.test.cpp, test/test.cmake
Adds tests for cache limits, deduplication, bounded lookups, product lookups, and exact/subsecond Nexrad-file retrieval, and wires them into wxtest.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RadarProductManager
  participant ProductDatastore
  participant ProviderManager
  RadarProductManager->>ProductDatastore: Populate product times
  ProductDatastore->>ProviderManager: Query provider volume times
  ProviderManager-->>ProductDatastore: Return volume time points
  RadarProductManager->>ProductDatastore: Find cached record
  ProductDatastore-->>RadarProductManager: Return record entry or NexradFile
Loading

Possibly related PRs

Suggested reviewers: dpaulat

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly points to the ProductDatastore extraction.
Description check ✅ Passed The description matches the changeset and accurately summarizes the datastore extraction and related refactor.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp (1)

730-742: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Copy the provider manager before releasing the map lock.

level3ProviderManager is an iterator used after unlocking. A concurrent insertion can rehash the map, invalidating it and causing undefined behavior.

Proposed fix
-   std::shared_lock providerManagerLock(p->level3ProviderManagerMutex_);
-   auto level3ProviderManager = p->level3ProviderManagerMap_.find(product);
-   if (level3ProviderManager == p->level3ProviderManagerMap_.cend())
    {
-      logger_->debug("No level 3 provider manager for product: {}", product);
-      return;
+      std::shared_lock lock {p->level3ProviderManagerMutex_};
+      const auto it = p->level3ProviderManagerMap_.find(product);
+      if (it == p->level3ProviderManagerMap_.cend())
+      {
+         logger_->debug("No level 3 provider manager for product: {}", product);
+         return;
+      }
+      level3ProviderManager = it->second;
    }
-   providerManagerLock.unlock();

    p->LoadProviderData(time,
-                       level3ProviderManager->second,
+                       level3ProviderManager,
                        product,

Declare std::shared_ptr<ProviderManager> level3ProviderManager; before the locked scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp` around lines 730 -
742, Copy the provider manager shared pointer from the map while
level3ProviderManagerMutex_ is held, then release the lock before calling
LoadProviderData. Replace the post-unlock iterator use with the copied
shared_ptr, preserving the existing missing-product return path.
🧹 Nitpick comments (1)
scwx-qt/source/scwx/qt/manager/product_datastore.cpp (1)

302-325: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify the recent-records update logic.

The current logic using the iteratorErased flag and multiple conditions is functionally correct but convoluted. It can be simplified by directly checking if the record needs to be moved to the front.

💡 Proposed refactor
    void
    UpdateRecentRecords(RadarProductRecordList& recentList,
                        const std::shared_ptr<types::RadarProductRecord>& record)
    {
       const std::size_t recentListMaxSize {cacheLimit_.load()};
-      bool              iteratorErased = false;
 
       auto it = std::find(recentList.cbegin(), recentList.cend(), record);
-      if (it != recentList.cbegin() && it != recentList.cend())
-      {
-         recentList.erase(it);
-         iteratorErased = true;
-      }
-
-      if (iteratorErased || recentList.size() == 0 || it != recentList.cbegin())
+      if (recentList.empty() || it != recentList.cbegin())
       {
+         if (it != recentList.cend())
+         {
+            recentList.erase(it);
+         }
          recentList.push_front(record);
       }
 
       while (recentList.size() > recentListMaxSize)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scwx-qt/source/scwx/qt/manager/product_datastore.cpp` around lines 302 - 325,
Refactor UpdateRecentRecords to remove the iteratorErased flag and consolidate
the conditions controlling whether the record is moved to the front. Directly
determine from the find result whether an existing non-front record should be
erased and reinserted, while preserving the behavior for a front record, an
absent record, and the cache-size trimming loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scwx-qt/source/scwx/qt/manager/product_datastore.cpp`:
- Around line 444-449: Update the volume-time population logic in Store() and
GetCachedNexradFile() to truncate each provider-supplied volumeTime to
std::chrono::seconds before using it as a cache key or inserting the
placeholder. Preserve the existing weak_ptr placeholder behavior while ensuring
these keys match the second-aligned keys produced by the existing cache paths.

In `@test/source/scwx/qt/manager/product_datastore.test.cpp`:
- Around line 9-11: In test/source/scwx/qt/manager/product_datastore.test.cpp at
lines 9-11, replace the C++17 nested namespace declaration with traditional
nested namespace blocks for scwx, qt, and manager; at lines 180-181, close them
with separate comments for manager, qt, and scwx.

---

Outside diff comments:
In `@scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp`:
- Around line 730-742: Copy the provider manager shared pointer from the map
while level3ProviderManagerMutex_ is held, then release the lock before calling
LoadProviderData. Replace the post-unlock iterator use with the copied
shared_ptr, preserving the existing missing-product return path.

---

Nitpick comments:
In `@scwx-qt/source/scwx/qt/manager/product_datastore.cpp`:
- Around line 302-325: Refactor UpdateRecentRecords to remove the iteratorErased
flag and consolidate the conditions controlling whether the record is moved to
the front. Directly determine from the find result whether an existing non-front
record should be erased and reinserted, while preserving the behavior for a
front record, an absent record, and the cache-size trimming loop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 97e95821-c0ef-4c78-beb0-4a8e0fd9dfd4

📥 Commits

Reviewing files that changed from the base of the PR and between 82ebaed and 21fcf42.

📒 Files selected for processing (6)
  • scwx-qt/scwx-qt.cmake
  • scwx-qt/source/scwx/qt/manager/product_datastore.cpp
  • scwx-qt/source/scwx/qt/manager/product_datastore.hpp
  • scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp
  • test/source/scwx/qt/manager/product_datastore.test.cpp
  • test/test.cmake

Comment on lines +444 to +449
[](const std::chrono::system_clock::time_point& volumeTime)
{
return std::pair<std::chrono::system_clock::time_point,
std::weak_ptr<types::RadarProductRecord>>(
volumeTime, std::weak_ptr<types::RadarProductRecord> {});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Ensure populated volume times are aligned to seconds.

In Store() and GetCachedNexradFile(), time keys are explicitly truncated to std::chrono::seconds. However, the volumeTime populated here is inserted without truncation. If the provider returns a time_point with sub-second precision, it won't match the keys generated by Store(), defeating the placeholder mechanism and resulting in duplicate cache entries.

Align the volumeTime to seconds before insertion to guarantee consistent cache keys.

🛡️ Proposed fix
-         [](const std::chrono::system_clock::time_point& volumeTime)
+         [](const std::chrono::system_clock::time_point& volumeTime)
          {
+            const auto volumeTimeInSeconds =
+               std::chrono::time_point_cast<std::chrono::seconds,
+                                            std::chrono::system_clock>(
+                  volumeTime);
+
             return std::pair<std::chrono::system_clock::time_point,
                              std::weak_ptr<types::RadarProductRecord>>(
-               volumeTime, std::weak_ptr<types::RadarProductRecord> {});
+               volumeTimeInSeconds, std::weak_ptr<types::RadarProductRecord> {});
          });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scwx-qt/source/scwx/qt/manager/product_datastore.cpp` around lines 444 - 449,
Update the volume-time population logic in Store() and GetCachedNexradFile() to
truncate each provider-supplied volumeTime to std::chrono::seconds before using
it as a cache key or inserting the placeholder. Preserve the existing weak_ptr
placeholder behavior while ensuring these keys match the second-aligned keys
produced by the existing cache paths.

Comment thread test/source/scwx/qt/manager/product_datastore.test.cpp
@dpaulat dpaulat added the coderabbit CodeRabbit should review this pull request label Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coderabbit CodeRabbit should review this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants