Class ThreadSafeEntityCache

Class Documentation

class ThreadSafeEntityCache

Thread-safe, index-optimized cache for SOVD entities.

Design principles:

  1. Primary storage in vectors (cache-friendly, predictable memory)

  2. Hash indexes for O(1) lookup by ID

  3. Relationship indexes for O(1) aggregation queries

  4. Reader-writer lock for concurrent access

  5. Batch updates to minimize lock contention

Thread Safety:

  • Multiple readers can access concurrently (shared lock)

  • Writers get exclusive access (unique lock)

  • Readers never block each other

  • Writers block all readers and other writers

Usage:

  • Discovery thread calls update_*() methods (writer)

  • HTTP handlers call get_*() methods (reader)

Public Functions

ThreadSafeEntityCache() = default
void update_all(std::vector<Area> areas, std::vector<Component> components, std::vector<App> apps, std::vector<Function> functions, std::unordered_map<std::string, std::string> node_to_app = {})

Atomic batch update of all entities.

This is the preferred update method as it:

  1. Minimizes lock hold time

  2. Ensures readers never see partial state

  3. Rebuilds all indexes in one pass

Parameters:
  • areas – All discovered areas

  • components – All discovered components

  • apps – All discovered apps

  • functions – All discovered functions

  • node_to_app – Node FQN to app entity ID mapping from linking result (optional)

void update_areas(std::vector<Area> areas)

Incremental update for single entity type.

Use sparingly - prefer update_all() for full refresh. Each call acquires exclusive lock and rebuilds relevant indexes.

void update_components(std::vector<Component> components)
void update_apps(std::vector<App> apps)
void update_functions(std::vector<Function> functions)
void update_topic_types(std::unordered_map<std::string, std::string> topic_types)

Update cached topic type mapping.

Called during cache refresh to cache topic name -> message type mapping. This avoids expensive ROS graph queries on every /data request.

Parameters:

topic_types – Map of topic name to message type

std::vector<Area> get_areas() const
std::vector<Component> get_components() const
std::vector<App> get_apps() const
std::vector<Function> get_functions() const
std::optional<Area> get_area(const std::string &id) const
std::optional<Component> get_component(const std::string &id) const
std::optional<App> get_app(const std::string &id) const
std::optional<Function> get_function(const std::string &id) const
bool has_area(const std::string &id) const
bool has_component(const std::string &id) const
bool has_app(const std::string &id) const
bool has_function(const std::string &id) const
std::string get_topic_type(const std::string &topic_name) const

Get cached message type for a topic.

Parameters:

topic_name – Full topic path (e.g., “/sensor/temperature”)

Returns:

Message type string, or empty if not cached

std::unordered_map<std::string, std::string> get_node_to_app() const

Get the node FQN to app entity ID mapping.

Used by trigger subscribers to resolve ROS 2 node FQNs to manifest entity IDs. The mapping is populated from the linking result during cache refresh.

Returns:

Map of node FQN -> app entity ID (empty if no linking available)

std::string resolve_node_to_app(const std::string &node_fqn) const

Look up a single node FQN to app entity ID mapping (shared lock, no map copy)

std::optional<EntityRef> find_entity(const std::string &id) const
SovdEntityType get_entity_type(const std::string &id) const
std::vector<std::string> get_apps_for_component(const std::string &component_id) const

Get Apps hosted on a Component.

Returns:

Vector of App IDs (empty if component not found)

std::vector<std::string> get_components_for_area(const std::string &area_id) const

Get Components in an Area.

Returns:

Vector of Component IDs (empty if area not found)

std::vector<std::string> get_apps_for_function(const std::string &function_id) const

Get Apps implementing a Function.

Returns:

Vector of App IDs (empty if function not found)

std::vector<std::string> get_subareas(const std::string &area_id) const

Get subareas of an Area.

Returns:

Vector of Area IDs (empty if area not found or no subareas)

AggregatedOperations get_app_operations(const std::string &app_id) const

Aggregate operations for an App (no aggregation, returns own ops)

AggregatedOperations get_component_operations(const std::string &component_id) const

Aggregate operations for a Component.

Returns: Component’s own operations + all operations from hosted Apps. Deduplicates by operation full_path.

AggregatedOperations get_area_operations(const std::string &area_id) const

Aggregate operations for an Area (x-medkit extension)

Returns: All operations from all Components in the Area. Recursive through Component→App hierarchy.

Note: This is a ros2_medkit extension. SOVD spec doesn’t allow Area to have resource collections.

AggregatedOperations get_function_operations(const std::string &function_id) const

Aggregate operations for a Function.

Returns: All operations from all Apps implementing this Function.

AggregatedData get_entity_data(const std::string &entity_id) const

Aggregate data (topics) for any entity by ID.

Unified method that works for all entity types. Aggregates topics from the entity and its children.

Parameters:

entity_id – Entity ID to get data for

Returns:

AggregatedData with topics, or empty if entity not found

AggregatedData get_app_data(const std::string &app_id) const

Aggregate data (topics) for an App.

AggregatedData get_component_data(const std::string &component_id) const

Aggregate data (topics) for a Component.

Returns: Component’s own topics + all topics from hosted Apps.

AggregatedData get_area_data(const std::string &area_id) const

Aggregate data (topics) for an Area.

Returns: All topics from all Components in the Area.

AggregatedData get_function_data(const std::string &function_id) const

Aggregate data (topics) for a Function.

Returns: All topics from all Apps implementing this Function.

AggregatedConfigurations get_entity_configurations(const std::string &entity_id) const

Aggregate configurations (node FQNs) for any entity by ID.

Unified method that works for all entity types. For Apps, returns the single bound node. For Components/Areas/Functions, aggregates all child app nodes.

Parameters:

entity_id – Entity ID to get configurations for

Returns:

AggregatedConfigurations with node FQNs, or empty if entity not found

AggregatedConfigurations get_app_configurations(const std::string &app_id) const

Get configurations for an App (returns its single bound node)

AggregatedConfigurations get_component_configurations(const std::string &component_id) const

Aggregate configurations for a Component.

Returns: All node FQNs from hosted Apps.

AggregatedConfigurations get_area_configurations(const std::string &area_id) const

Aggregate configurations for an Area.

Returns: All node FQNs from all Components in the Area.

AggregatedConfigurations get_function_configurations(const std::string &function_id) const

Aggregate configurations for a Function.

Returns: All node FQNs from all Apps implementing this Function.

std::optional<EntityRef> find_operation_owner(const std::string &operation_path) const

Find which entity owns an operation by full path.

Parameters:

operation_path – e.g., “/nav2/navigate_to_pose”

Returns:

EntityRef if found

EntityCacheStats get_stats() const

Get cache statistics.

std::string validate() const

Validate internal consistency.

Checks:

  1. All indexes point to valid vector entries

  2. Relationship indexes are consistent

  3. No duplicate IDs within entity type

Returns:

Empty string if valid, error message otherwise

std::chrono::system_clock::time_point get_last_update() const

Get last update timestamp.

uint64_t generation() const

Get the current generation counter.

The generation counter is incremented on every cache mutation (update_all, update_areas, update_components, update_apps, update_functions). Consumers can use this to detect when cached derived data (e.g., OpenAPI specs) is stale and needs regeneration.