feat: add frozen flag for complete immutability and simplify README examples
- Add frozen flag to separate data immutability from performance optimizations - readOnly: prevents data mutations but allows index optimizations (default behavior) - frozen: prevents ALL changes including statistics and index updates - Smart default: frozen=false when readOnly=true for optimal performance - Add comprehensive documentation for read-only and frozen modes - Created docs/guides/readonly-frozen-modes.md with detailed guide - Added examples for compliance, forensics, and testing use cases - Updated all documentation indexes with new guide links - Simplify README.md to emphasize unified API - Clearer demonstration that same code works everywhere - Simplified framework examples showing consistent API - Better noun/verb examples for entities and relationships - Collapsible sections for cloud platform examples - Environment auto-detection table - Add tests for frozen flag behavior - Test readOnly without frozen (allows optimizations) - Test frozen mode (complete immutability) - Test dynamic mode switching BREAKING CHANGE: readOnly behavior changed - now allows optimizations by default. To get old behavior (complete immutability), use readOnly: true with frozen: true.
This commit is contained in:
parent
8976f274f3
commit
e838327a22
6 changed files with 726 additions and 570 deletions
|
|
@ -137,9 +137,18 @@ export interface BrainyDataConfig {
|
|||
/**
|
||||
* Set the database to read-only mode
|
||||
* When true, all write operations will throw an error
|
||||
* Note: Statistics and index optimizations are still allowed unless frozen is also true
|
||||
*/
|
||||
readOnly?: boolean
|
||||
|
||||
/**
|
||||
* Completely freeze the database, preventing all changes including statistics and index optimizations
|
||||
* When true, the database is completely immutable (no data changes, no index rebalancing, no statistics updates)
|
||||
* This is useful for forensic analysis, testing with deterministic state, or compliance scenarios
|
||||
* Default: false (allows optimizations even in readOnly mode)
|
||||
*/
|
||||
frozen?: boolean
|
||||
|
||||
/**
|
||||
* Enable lazy loading in read-only mode
|
||||
* When true and in read-only mode, the index is not fully loaded during initialization
|
||||
|
|
@ -370,6 +379,7 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
private distanceFunction: DistanceFunction
|
||||
private requestPersistentStorage: boolean
|
||||
private readOnly: boolean
|
||||
private frozen: boolean
|
||||
private lazyLoadInReadOnlyMode: boolean
|
||||
private writeOnly: boolean
|
||||
private storageConfig: BrainyDataConfig['storage'] = {}
|
||||
|
|
@ -488,6 +498,9 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
// Set read-only flag
|
||||
this.readOnly = config.readOnly || false
|
||||
|
||||
// Set frozen flag (defaults to false to allow optimizations in readOnly mode)
|
||||
this.frozen = config.frozen || false
|
||||
|
||||
// Set lazy loading in read-only mode flag
|
||||
this.lazyLoadInReadOnlyMode = config.lazyLoadInReadOnlyMode || false
|
||||
|
||||
|
|
@ -602,6 +615,18 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is frozen and throw an error if it is
|
||||
* @throws Error if the database is frozen
|
||||
*/
|
||||
private checkFrozen(): void {
|
||||
if (this.frozen) {
|
||||
throw new Error(
|
||||
'Cannot perform operation: database is frozen (no changes allowed)'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is in write-only mode and throw an error if it is
|
||||
* @param allowExistenceChecks If true, allows existence checks (get operations) in write-only mode
|
||||
|
|
@ -625,6 +650,14 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
return
|
||||
}
|
||||
|
||||
// If the database is frozen, do not start real-time updates
|
||||
if (this.frozen) {
|
||||
if (this.loggingConfig?.verbose) {
|
||||
console.log('Real-time updates disabled: database is frozen')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If the update timer is already running, do nothing
|
||||
if (this.updateTimerId !== null) {
|
||||
return
|
||||
|
|
@ -739,6 +772,11 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
return
|
||||
}
|
||||
|
||||
// If the database is frozen, do not perform updates
|
||||
if (this.frozen) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Record the current time
|
||||
const startTime = Date.now()
|
||||
|
|
@ -3873,6 +3911,11 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
throw new Error('Storage not initialized')
|
||||
}
|
||||
|
||||
// If the database is frozen, do not flush statistics
|
||||
if (this.frozen) {
|
||||
return
|
||||
}
|
||||
|
||||
// Call the flushStatisticsToStorage method on the storage adapter
|
||||
await this.storage.flushStatisticsToStorage()
|
||||
}
|
||||
|
|
@ -3881,6 +3924,11 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
* Update storage sizes if needed (called periodically for performance)
|
||||
*/
|
||||
private async updateStorageSizesIfNeeded(): Promise<void> {
|
||||
// If the database is frozen, do not update storage sizes
|
||||
if (this.frozen) {
|
||||
return
|
||||
}
|
||||
|
||||
// Only update every minute to avoid performance impact
|
||||
const now = Date.now()
|
||||
const lastUpdate = (this as any).lastStorageSizeUpdate || 0
|
||||
|
|
@ -3963,8 +4011,8 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
await this.ensureInitialized()
|
||||
|
||||
try {
|
||||
// If forceRefresh is true, flush statistics to storage first
|
||||
if (options.forceRefresh && this.storage) {
|
||||
// If forceRefresh is true and not frozen, flush statistics to storage first
|
||||
if (options.forceRefresh && this.storage && !this.frozen) {
|
||||
await this.storage.flushStatisticsToStorage()
|
||||
}
|
||||
|
||||
|
|
@ -4150,6 +4198,32 @@ export class BrainyData<T = any> implements BrainyDataInterface<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is frozen (completely immutable)
|
||||
* @returns True if the database is frozen, false otherwise
|
||||
*/
|
||||
public isFrozen(): boolean {
|
||||
return this.frozen
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database to frozen mode (completely immutable)
|
||||
* When frozen, no changes are allowed including statistics updates and index optimizations
|
||||
* @param frozen True to freeze the database, false to allow optimizations
|
||||
*/
|
||||
public setFrozen(frozen: boolean): void {
|
||||
this.frozen = frozen
|
||||
|
||||
// If unfreezing and real-time updates are configured, restart them
|
||||
if (!frozen && this.realtimeUpdateConfig.enabled && this.isInitialized) {
|
||||
this.startRealtimeUpdates()
|
||||
}
|
||||
// If freezing, stop real-time updates
|
||||
else if (frozen && this.updateTimerId !== null) {
|
||||
this.stopRealtimeUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is in write-only mode
|
||||
* @returns True if the database is in write-only mode, false otherwise
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue