Caching, Incremental Synchronization & Search¶
Arodonata v2 coordinates multi-domain synchronization through DomainSyncCoordinator (src/arodonata/core/services/sync_coordinator.py) and exposes four cache freshness modes (CacheMode).
1. The Four CacheMode Policies¶
| Mode | Staleness Probe (show-last-published-session) | Data Fetch Strategy | Typical Use Case |
|---|---|---|---|
"cache" | Never | Local SQLite/PostgreSQL query only (0 API calls) | High-speed rulebase filtering, offline analysis, MCP query_objects |
"smart" (default) | Only when synced_at is older than cache_ttl (or after invalidate_domain) | Full atomic type-slice reload if (last_published_at, last_session_uid) changed | Default SDK reads (get_hosts, get_services, get_gateways) |
"smart-fast" | Same as "smart" | Queries show-changes (from-session or UTC from-date) and applies dialect ON CONFLICT upserts/deletes; falls back to full reload if changes > max_incremental_changes (500) | Rapid refresh after small publishes across large domains |
"force" | Always probes current publish head | Unconditional atomic reload of all requested types | Initial cache build (04_smart_refresh.py) or forced recovery |
2. Synchronization Pipeline & Safeguards¶
sequenceDiagram
participant Caller as Caller / ArodonataClient
participant Coord as DomainSyncCoordinator
participant Lock as SqlLockAdapter (arodonata_distributed_locks)
participant API as AMgmtClient (Check Point API)
participant Cache as SqlCacheAdapter (PostgreSQL / SQLite)
Caller->>Coord: ensure(scope="objects", mode="smart-fast")
Coord->>Cache: list_sync_states(mgmt, domain, scope)
alt Within cache_ttl and not invalidated
Coord-->>Caller: Return cached data immediately (0 API calls)
else Expired TTL or invalidated
Coord->>Lock: acquire("sync:mgmt:domain:objects") + start heartbeat task
Coord->>API: show-session (verify shared session has 0 uncommitted changes)
Coord->>API: show-last-published-session
alt (last_published_at, last_session_uid) unchanged
Coord->>Cache: Update synced_at timestamp
else Published head advanced
alt mode == "smart-fast" and baseline exists
Coord->>API: show-changes(from-session=last_session_uid)
Coord->>Cache: Atomic transaction: apply_incremental_objects + upsert_sync_state
else mode == "smart" or "force" or >500 changes
Coord->>API: Continuity-verified paginated show-hosts / show-networks / ...
Coord->>Cache: Atomic transaction: replace_domain_objects + upsert_sync_state
end
end
Coord->>Lock: Cancel heartbeat + release lock
Coord-->>Caller: RefreshOutcome
end Critical Invariants¶
- Dirty Shared Session Refusal (
DirtySessionError): Before syncing any domain into the shared cache,DomainSyncCoordinatorverifies viashow-sessionthat the session haschanges == 0. It never caches uncommitted objects into the shared SQL tables. - Per-MDS-Member Concurrency: Domains hosted on the same
active_mds_ipare refreshed concurrently viaasyncio.TaskGroupup tomax(1, concurrent_limit - 1)at a time, leaving at least one rate-limiter slot free for interactive API calls. - Lock Contention vs Genuine Failure: If another worker holds the per-domain lock (
LockAcquisitionError), the coordinator skips redundant work without markingarodonata_domain_sync_state.status = "failed". Genuine API or credential errors record a sanitized (redact_sid) error inlast_errorwhile preserving previously cached rows.
3. Search & Recursive Group Membership (SearchService)¶
await client.search_objects(query, ..., exact_match=False, max_depth=2) classifies each comma-separated term in query and searches across arodonata_objects (plus any active WriteSession overlay):
- IPv4 Address (
10.1.10.50): Matches hosts with that IPv4 address, networks whose CIDR subnet contains the IP, and address ranges whose[first..last]bounds include the IP. - CIDR Subnet (
10.1.10.0/24): Matches networks with that subnet/mask and hosts/subnets contained within it. - IPv4 Range (
10.1.10.1-10.1.10.20): Matches overlappingaddress-rangeobjects and contained hosts. - Wildcard Pattern (
*.corp.example,web-*): Evaluates case-insensitive anchoredfnmatchcaseacross object names andfqdnfields. - Composite Identifier (
mds:Domain4:hWebHostormds:Domain4:<uid>): MatchesCPObject.object_idorCPObject.iddirectly (case-insensitively onobject_id). - Exact or Substring Name (
exact_match=TruevsFalse): - When
exact_match=False(default), matches case-insensitive substrings inname,fqdn,object_id, andcomments. - When
exact_match=True, matches only exact case-insensitivename,fqdn,object_id, orid(e.g., searching".example.com"withexact_match=Truematches.example.comwithout matchingsub.example.com). - Recursive Group Resolution (
max_depth): Automatically traversesgroup.membersupward up tomax_depthlevels, returning bothSearchResult.objectsandSearchResult.memberships(object_uid -> list[GroupMembershipNode], where eachGroupMembershipNodecarriesuid,name,type,domain,id,object_id,depth,parent_uids, andparent_object_ids).
4. Cross-Domain Asset Topology & Composite Identifiers¶
During refresh_assets / stream_refresh_assets, AssetRefreshService runs ClusterRelationshipManager and VSXRelationshipManager across cached domain assets:
- Intra-Domain Cluster Members: Links each cluster (
CpmiGatewayCluster,vsx-cluster-netobj) and its members (CpmiClusterMember,vsx-cluster-member) with both UID references (parent_uid,member_uids) and composite identifiers (parent_id,parent_asset_id,member_asset_ids). - Cross-Domain VSX Virtual Systems: On Multi-Domain Security Management (MDSM), a physical VSX gateway or cluster typically lives in an infrastructure domain (e.g.,
"VSX") while its Virtual Systems (virtual-system) reside in tenant domains (e.g.,"Domain4").AssetRefreshServicereconciles VSX relationships across all domains on the samemgmt_name, populatingparent_id("mds:VSX:<vsx-uid>"),parent_asset_id("mds:VSX:<vsx-name>"),virtual_system_uids, andvirtual_system_asset_ids(["mds:Domain4:<vs-name>"]).