Local Caching
Relevant source files
The following files were used as context for generating this wiki page:
The Local Caching system is a foundational component of BMSview's local-first sync architecture. It provides a persistent data layer within the user's browser, leveraging IndexedDB through the Dexie.js library. The primary purpose of this system is to enable offline functionality, improve application performance by reducing server requests, and serve as the local source of truth for data that is periodically synchronized with the backend.
This system is orchestrated by the SyncManager and is central to the application's data fetching and storage strategies. It ensures a responsive user experience by serving data directly from the local cache, only falling back to the network when necessary. All data interactions, including reads, writes, and metadata tracking, are managed through this module.
Sources: ARCHITECTURE.md:10-14, GEMINI.md:29-32, PHASE_COMPLETION_VERIFICATION.md:19-21
Architecture
The LocalCache module is designed as a robust wrapper around IndexedDB, providing a structured and consistent API for data persistence and synchronization state management. It is a key dependency for higher-level services like the SyncManager and ClientService.
Sources: ARCHITECTURE.md:43-61, GEMINI.md:41-43
This diagram illustrates the LocalCache's place within the frontend architecture.
Sources: ARCHITECTURE.md:18-61
IndexedDB Schema
The local database is managed by Dexie.js and consists of several collections (object stores) designed to hold different types of application data. Schema versioning is handled by Dexie.js to manage migrations.
Sources: PHASE_COMPLETION_VERIFICATION.md:22-24, src/services/localCache.ts:16-29
The schema is defined in localCache.ts:
// From: src/services/localCache.ts:16-29
class LocalCache extends Dexie {
systems!: Table<SystemCacheEntry, string>;
history!: Table<HistoryCacheEntry, string>;
analytics!: Table<AnalyticsCacheEntry, string>;
weather!: Table<WeatherCacheEntry, string>;
metadata!: Table<MetadataEntry, string>;
constructor() {
super('BMSViewDB');
this.version(1).stores({
systems: 'id, updatedAt, _syncStatus',
history: 'id, updatedAt, _syncStatus, systemId, timestamp',
analytics: 'id, updatedAt, _syncStatus',
weather: 'id, timestamp',
metadata: 'collectionName',
});
}
}
The following diagram shows the entity relationships within the local IndexedDB schema.
Data Record Structure
Every record stored in the main collections (systems, history, analytics) follows a standardized structure to support the synchronization process. This includes the actual data payload and several metadata fields.
| Field | Type | Description |
|---|---|---|
id | string | Primary key for the record. |
data | object | The actual data object for the entry. |
updatedAt | string | ISO 8601 UTC timestamp of the last server-side update. Used for conflict resolution. |
_syncStatus | 'pending' | 'synced' | Tracks the synchronization state of the record. 'pending' means it needs to be pushed to the server. |
timestamp | string | (Optional) Creation timestamp. |
checksum | string | (Optional) A content hash for deduplication purposes. |
Sources: ARCHITECTUREকেmd:100-110, PHASE_COMPLETION_VERIFICATION.md:26-30
A strict UTC timestamp format is enforced on writes to ensure consistency.
const UTC_TIMESTAMP_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
Sources: ARCHITECTURE.md:113-114
Key Operations
The LocalCache class provides a comprehensive set of methods for interacting with the IndexedDB. These methods are grouped into CRUD operations, sync tracking, and metadata management.
Sources: src/services/localCache.ts, PHASE_COMPLETION_VERIFICATION.md:31-36
Core Methods
| Method | Description |
|---|---|
put(item, status) | Adds or updates a single record in a collection, setting its sync status. |
get(id) | Retrieves a single record by its primary key. |
bulkPut(items, status) | Adds or updates an array of items efficiently. |
delete(id) | Deletes a record by its primary key. |
markAsSynced(id, serverTimestamp) | Updates a record's _syncStatus to 'synced' and sets its updatedAt timestamp. |
getPendingItems() | Retrieves all records across collections with a _syncStatus of 'pending'. |
getMetadata(collectionName) | Returns sync metadata (lastModified, recordCount, checksum) for a collection. |
getStalenessInfo() | Checks if the cache is considered stale based on timestamps. |
Sources: ARCHITECTURE.md:117-129, tests/localCache.test.js
Data Flow: Cache-First Read
Read operations follow a cache-first strategy to maximize performance and enable offline access. The ClientService first attempts to retrieve data from LocalCache. If the data is not present (a cache miss), it fetches the data from the server and populates the cache for future requests.
This flowchart shows the logic for a cache-first data fetch.
Sources: ARCHITECTURE.md:72-75
Synchronization State
A critical feature of the LocalCache is its ability to track the synchronization state of each record. This is managed by the _syncStatus field.
synced: The record is up-to-date with the server.pending: The record has been created or modified locally and needs to be pushed to the server during the next sync cycle.
The SyncManager uses the getPendingItems() method to collect all records marked as 'pending' and pushes them to the backend. After a successful push, the markAsSynced() method is called to update the status and server-provided timestamp.
Sources: ARCHITECTURE.md:104, src/services/syncManager.ts:46-51, PHASE_COMPLETION_VERIFICATION.md:34
Testing
The local caching system is extensively tested to ensure its reliability. The test suite covers all primary functionalities.
- CRUD Operations: Verifies that create, read, update, and delete operations work as expected for both single and bulk actions.
- Metadata Retrieval: Ensures that
getMetadatacorrectly calculatesrecordCountandlastModifiedtimestamps. - Sync Status Management: Tests the logic for marking items as pending or synced.
- Staleness Detection: Validates the logic for determining if the cache is out of date.
- Bulk Operations: Confirms that
bulkPutandbulkDeletehandle large datasets efficiently.
Sources: tests/localCache.test.js, PHASE_COMPLETION_VERIFICATION.md:38-44
Conclusion
The LocalCache module is the cornerstone of BMSview's local-first architecture. By providing a well-defined, reliable, and tested API for browser-side data persistence, it enables a fast, offline-capable user experience. Its tight integration with the SyncManager and its robust handling of synchronization states and metadata are crucial for the intelligent sync strategy that significantly reduces server load and ensures data consistency between the client and server.
Sources: ARCHITECTURE.md:10-14, ISSUE_176_FINAL_SUMMARY.md:17-19