@soulcraft/brainy 3.32.1 β 3.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [3.32.2](https://github.com/soulcraftlabs/brainy/compare/v3.32.1...v3.32.2) (2025-10-09)
|
|
6
|
+
|
|
7
|
+
### π Critical Bug Fixes - Container Restart Persistence
|
|
8
|
+
|
|
9
|
+
**Fixed: brain.find({ where: {...} }) returns empty array after restart**
|
|
10
|
+
**Fixed: brain.init() returns 0 entities after container restart**
|
|
11
|
+
|
|
12
|
+
#### Root Cause
|
|
13
|
+
Count persistence was optimized to save only every 10 operations. If <10 entities were added before container restart, counts were never persisted to storage. After restart: `totalNounCount = 0`, causing empty query results.
|
|
14
|
+
|
|
15
|
+
#### Impact
|
|
16
|
+
Critical for serverless/containerized deployments (Cloud Run, Fargate, Lambda) where containers restart frequently. The basic writeβrestartβread scenario was broken.
|
|
17
|
+
|
|
18
|
+
#### Changes
|
|
19
|
+
- `baseStorageAdapter.ts`: Persist counts on EVERY operation (not every 10)
|
|
20
|
+
- `incrementEntityCountSafe()`: Now persists immediately
|
|
21
|
+
- `decrementEntityCountSafe()`: Now persists immediately
|
|
22
|
+
- `incrementVerbCount()`: Now persists immediately
|
|
23
|
+
- `decrementVerbCount()`: Now persists immediately
|
|
24
|
+
|
|
25
|
+
- `gcsStorage.ts`: Better error handling for count initialization
|
|
26
|
+
- `initializeCounts()`: Fail loudly on network/permission errors
|
|
27
|
+
- `initializeCountsFromScan()`: Throw on scan failures instead of silent fail
|
|
28
|
+
- Added recovery logic with bucket scan fallback
|
|
29
|
+
|
|
30
|
+
#### Test Scenario (Now Fixed)
|
|
31
|
+
```typescript
|
|
32
|
+
// Service A: Add 2 entities
|
|
33
|
+
await brain.add({ data: 'Entity 1' })
|
|
34
|
+
await brain.add({ data: 'Entity 2' })
|
|
35
|
+
|
|
36
|
+
// Container restarts (Cloud Run, Fargate, etc.)
|
|
37
|
+
|
|
38
|
+
// Service B: Query data
|
|
39
|
+
const stats = await brain.getStats()
|
|
40
|
+
console.log(stats.entities.total) // Was: 0 β | Now: 2 β
|
|
41
|
+
|
|
42
|
+
const results = await brain.find({ where: { status: 'active' }})
|
|
43
|
+
console.log(results.length) // Was: 0 β | Now: 2 β
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
5
48
|
## [3.31.0](https://github.com/soulcraftlabs/brainy/compare/v3.30.2...v3.31.0) (2025-10-09)
|
|
6
49
|
|
|
7
50
|
### π Critical Bug Fixes - Production-Scale Import Performance
|
|
@@ -659,10 +659,10 @@ export class BaseStorageAdapter {
|
|
|
659
659
|
const mutex = getGlobalMutex();
|
|
660
660
|
await mutex.runExclusive(`count-entity-${type}`, async () => {
|
|
661
661
|
this.incrementEntityCount(type);
|
|
662
|
-
// Persist counts
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
662
|
+
// CRITICAL FIX: Persist counts on EVERY change for cloud storage adapters
|
|
663
|
+
// This ensures counts survive container restarts (GCS, S3, etc.)
|
|
664
|
+
// For memory/file storage, this is fast; for cloud storage, it's essential
|
|
665
|
+
await this.persistCounts();
|
|
666
666
|
});
|
|
667
667
|
}
|
|
668
668
|
/**
|
|
@@ -693,9 +693,8 @@ export class BaseStorageAdapter {
|
|
|
693
693
|
const mutex = getGlobalMutex();
|
|
694
694
|
await mutex.runExclusive(`count-entity-${type}`, async () => {
|
|
695
695
|
this.decrementEntityCount(type);
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
696
|
+
// CRITICAL FIX: Persist counts on EVERY change for cloud storage adapters
|
|
697
|
+
await this.persistCounts();
|
|
699
698
|
});
|
|
700
699
|
}
|
|
701
700
|
/**
|
|
@@ -712,10 +711,8 @@ export class BaseStorageAdapter {
|
|
|
712
711
|
count: this.totalVerbCount,
|
|
713
712
|
timestamp: Date.now()
|
|
714
713
|
});
|
|
715
|
-
// Persist counts
|
|
716
|
-
|
|
717
|
-
await this.persistCounts();
|
|
718
|
-
}
|
|
714
|
+
// CRITICAL FIX: Persist counts on EVERY change for cloud storage adapters
|
|
715
|
+
await this.persistCounts();
|
|
719
716
|
});
|
|
720
717
|
}
|
|
721
718
|
/**
|
|
@@ -740,10 +737,8 @@ export class BaseStorageAdapter {
|
|
|
740
737
|
count: this.totalVerbCount,
|
|
741
738
|
timestamp: Date.now()
|
|
742
739
|
});
|
|
743
|
-
// Persist counts
|
|
744
|
-
|
|
745
|
-
await this.persistCounts();
|
|
746
|
-
}
|
|
740
|
+
// CRITICAL FIX: Persist counts on EVERY change for cloud storage adapters
|
|
741
|
+
await this.persistCounts();
|
|
747
742
|
});
|
|
748
743
|
}
|
|
749
744
|
}
|
|
@@ -1095,8 +1095,8 @@ export class GcsStorage extends BaseStorage {
|
|
|
1095
1095
|
* Initialize counts from storage
|
|
1096
1096
|
*/
|
|
1097
1097
|
async initializeCounts() {
|
|
1098
|
+
const key = `${this.systemPrefix}counts.json`;
|
|
1098
1099
|
try {
|
|
1099
|
-
const key = `${this.systemPrefix}counts.json`;
|
|
1100
1100
|
const file = this.bucket.file(key);
|
|
1101
1101
|
const [contents] = await file.download();
|
|
1102
1102
|
const counts = JSON.parse(contents.toString());
|
|
@@ -1104,16 +1104,21 @@ export class GcsStorage extends BaseStorage {
|
|
|
1104
1104
|
this.totalVerbCount = counts.totalVerbCount || 0;
|
|
1105
1105
|
this.entityCounts = new Map(Object.entries(counts.entityCounts || {}));
|
|
1106
1106
|
this.verbCounts = new Map(Object.entries(counts.verbCounts || {}));
|
|
1107
|
-
prodLog.info(`π Loaded counts: ${this.totalNounCount} nouns, ${this.totalVerbCount} verbs`);
|
|
1107
|
+
prodLog.info(`π Loaded counts from storage: ${this.totalNounCount} nouns, ${this.totalVerbCount} verbs`);
|
|
1108
1108
|
}
|
|
1109
1109
|
catch (error) {
|
|
1110
1110
|
if (error.code === 404) {
|
|
1111
|
-
// No counts file yet - initialize from scan
|
|
1112
|
-
prodLog.info('π No counts file found -
|
|
1111
|
+
// No counts file yet - initialize from scan (first-time setup or counts not persisted)
|
|
1112
|
+
prodLog.info('π No counts file found - this is normal for first init or if <10 entities were added');
|
|
1113
1113
|
await this.initializeCountsFromScan();
|
|
1114
1114
|
}
|
|
1115
1115
|
else {
|
|
1116
|
-
|
|
1116
|
+
// CRITICAL FIX: Don't silently fail on network/permission errors
|
|
1117
|
+
this.logger.error('β CRITICAL: Failed to load counts from GCS:', error);
|
|
1118
|
+
prodLog.error(`β Error loading ${key}: ${error.message}`);
|
|
1119
|
+
// Try to recover by scanning the bucket
|
|
1120
|
+
prodLog.warn('β οΈ Attempting recovery by scanning GCS bucket...');
|
|
1121
|
+
await this.initializeCountsFromScan();
|
|
1117
1122
|
}
|
|
1118
1123
|
}
|
|
1119
1124
|
}
|
|
@@ -1122,6 +1127,7 @@ export class GcsStorage extends BaseStorage {
|
|
|
1122
1127
|
*/
|
|
1123
1128
|
async initializeCountsFromScan() {
|
|
1124
1129
|
try {
|
|
1130
|
+
prodLog.info('π Scanning GCS bucket to initialize counts...');
|
|
1125
1131
|
// Count nouns
|
|
1126
1132
|
const [nounFiles] = await this.bucket.getFiles({ prefix: this.nounPrefix });
|
|
1127
1133
|
this.totalNounCount = nounFiles?.filter((f) => f.name?.endsWith('.json')).length || 0;
|
|
@@ -1130,10 +1136,12 @@ export class GcsStorage extends BaseStorage {
|
|
|
1130
1136
|
this.totalVerbCount = verbFiles?.filter((f) => f.name?.endsWith('.json')).length || 0;
|
|
1131
1137
|
// Save initial counts
|
|
1132
1138
|
await this.persistCounts();
|
|
1133
|
-
prodLog.info(`β
Initialized counts: ${this.totalNounCount} nouns, ${this.totalVerbCount} verbs`);
|
|
1139
|
+
prodLog.info(`β
Initialized counts from scan: ${this.totalNounCount} nouns, ${this.totalVerbCount} verbs`);
|
|
1134
1140
|
}
|
|
1135
1141
|
catch (error) {
|
|
1136
|
-
|
|
1142
|
+
// CRITICAL FIX: Don't silently fail - this prevents data loss scenarios
|
|
1143
|
+
this.logger.error('β CRITICAL: Failed to initialize counts from GCS bucket scan:', error);
|
|
1144
|
+
throw new Error(`Failed to initialize GCS storage counts: ${error}. This prevents container restarts from working correctly.`);
|
|
1137
1145
|
}
|
|
1138
1146
|
}
|
|
1139
1147
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.32.
|
|
3
|
+
"version": "3.32.2",
|
|
4
4
|
"description": "Universal Knowledge Protocolβ’ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns Γ 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|