Tags: orneryd/NornicDB
Tags
perf(release): document untagged latest changes Add a consolidated untagged changelog for the latest image build (WAL durability hardening, Bolt/PackStream perf+correctness, Cypher parsing/traversal optimizations, storage/Badger cleanups) and link it from CHANGELOG.md.
refactor(multidb): enforce strict namespacing end-to-end and fix asyn… …c/embed correctness - Enforce namespaced IDs at the storage layer (no unprefixed keys) - Persist db name in WAL entries and replay per-entry database on recovery - Make search + k-means clustering per-database; remove default-only search service - Make inference/Auto‑TLP per-database; remove default-only inference service - Fix AsyncEngine flush/count races and embedding-update counting - Fix nil GPU manager panic in clustered search - Fix stale embeddings/indexes when deleting nodes before async flush commits - Prevent system DB leakage into default queries; harden manager wiring + cleanup - Add regression tests covering routing, WAL/async, and embedding/index cleanup
v1.0.9 - GraphQL API & Neo4j Compatibility Improvements Features: - Add full GraphQL API with gqlgen (schema, resolvers, introspection) - GraphQL endpoints: /graphql, /graphql/playground - Complete CRUD mutations (createNode, updateNode, deleteNode, merge, bulk ops) - GraphQL queries: search, similar, cypher, stats, schema, labels - Admin mutations: triggerEmbedding, rebuildSearchIndex, runDecay, clearAll Fixes: - Fix Cypher map projection returns without explicit AS aliases - Fix aggregation query alias handling in RETURN clauses - Fix embed queue pending embeddings index tracking - Improve Neo4j driver compatibility in UI Browser responses Tests: - Add GraphQL resolver and handler tests - Add storage pending embeddings index tests - Add node needs embedding tests - Add Cypher return alias tests Docs: - Add GraphQL README with usage examples - Add QUERIES.md with sample GraphQL operations
Release v1.0.7 (2025-12-14) Added: - Documentation: `DIY.md` with Vulkan/Makefile build instructions and targets. Changed: - Makefile/docs: added AMD64 Vulkan build targets and documented usage. Fixed: - Neo4j compatibility: return `*storage.Node` and `*storage.Edge` directly from Cypher query results with correct Bolt protocol serialization and property access handling. - Neo4j compatibility: improved integer handling in Cypher/driver interoperability. See CHANGELOG.md for full details
Release v1.0.6 (2025-12-12) Added: - Timer-based K-means clustering scheduler; runs on startup and periodically (default 15m) - New config: NORNICDB_KMEANS_CLUSTER_INTERVAL Changed: - Switched K-means from embed-queue trigger to timer-based scheduler; skips runs when no embedding changes - DB lifecycle starts/stops clustering ticker cleanly Fixed: - Prevent excessive K-means executions - Cypher YIELD/RETURN compatibility: RETURN after YIELD for property access, ORDER BY/LIMIT/SKIP handling Tests: - Added pkg/cypher/yield_return_test.go and updated neo4j compatibility tests See CHANGELOG.md for full details
- **Critical: Node/Edge Count Returns 0 After Delete+Recreate Cycles*… …* - `MATCH (n) RETURN count(n)` returned 0 even when nodes existed in the database - Atomic counters (`nodeCount`, `edgeCount`) in `BadgerEngine` became out of sync during delete+recreate cycles - Root cause: Nodes created via implicit transactions (MERGE, CREATE) bypass `CreateNode()` and use `UpdateNode()` - `UpdateNode()` checks if key exists to determine `wasInsert=true/false`, only incrementing counter when `wasInsert=true` - During delete+recreate, keys could still exist in BadgerDB from previous sessions, causing `wasInsert=false` for genuinely new nodes - The counter would increment for only 1 node, leaving `nodeCount=1` even with 234 nodes in the database - **Production symptom**: After deleting all nodes and re-importing 234 nodes via MERGE, `/metrics` showed `nornicdb_nodes_total 0` while `MATCH (n:Label)` returned all 234 nodes correctly - **Solution**: Changed `BadgerEngine.NodeCount()` and `EdgeCount()` to scan actual keys with prefix iteration instead of trusting atomic counter - Key-only iteration (no value loading) provides fast O(n) counting with guaranteed correctness - Atomic counter is updated after each scan to keep it synchronized for future calls - **Impact**: Count queries now always reflect reality. Embeddings worked because they scan actual data; node counts failed because they used broken counter. - **Critical: Aggregation Query Caching Caused Stale Counts** - `MATCH (n) RETURN count(n)` was being cached, returning stale results after node creation/deletion - `SmartQueryCache` was caching aggregation queries (COUNT, SUM, AVG, etc.) which must always be fresh - Modified `pkg/cypher/executor.go` to detect aggregation via `HasAggregation` flag and skip caching entirely - Modified `pkg/cypher/query_info.go` to analyze queries and set `HasAggregation=true` for COUNT/SUM/AVG/MIN/MAX/COLLECT - Modified `pkg/cypher/cache.go` to invalidate queries with no labels when any node is created/deleted (affects `MATCH (n)` count queries) - **Impact**: Count queries always return fresh data, no manual cache clear needed - **Performance Trade-off: NodeCount() and EdgeCount() Now O(n)** - Changed from O(1) atomic counter to O(n) key scan for correctness - Key-only iteration is very fast (no value decoding, just prefix scan) - BadgerDB iterators are highly optimized for this access pattern - Correctness > speed for core count operations - Future optimization: Maintain accurate counter through all write paths - Modified `pkg/storage/badger.go`: - `NodeCount()` now uses `BadgerDB.View()` with key-only iterator (`PrefetchValues=false`) - `EdgeCount()` uses same pattern for edge prefix scan - Both methods sync atomic counter after scan to reduce overhead on subsequent calls - Modified `pkg/storage/async_engine.go`: - Fixed `NodeCount()` calculation to include `inFlightCreates` to prevent race condition during flush - Prevented double-counting during flush window when nodes transition from cache to engine - Modified `pkg/cypher/executor.go`: - Added aggregation detection to skip caching for COUNT/SUM/AVG/MIN/MAX queries - Modified `pkg/cypher/query_info.go`: - Added `HasAggregation` field to `QueryInfo` struct - Updated `analyzeQuery()` to detect aggregation functions - Modified `pkg/cypher/cache.go`: - Fixed `InvalidateLabels()` to also invalidate queries with no labels (e.g., `MATCH (n)`) - All existing tests pass with updated expectations - Modified `pkg/storage/realtime_count_test.go` to account for transient over-counting before flush - Modified `pkg/cypher/executor_cache_test.go` to use non-aggregation queries (since aggregations aren't cached) - Added comprehensive logging and debugging to trace count calculation flow - Production issue validated as fixed: 234 nodes now counted correctly after delete+reimport
- **Critical: ORDER BY Ignored for Relationship Patterns** - `ORDER B…
…Y`, `SKIP`, and `LIMIT` clauses were completely ignored for queries with relationship patterns
- Queries like `MATCH (p:Person)-[:WORKS_IN]->(a:Area) RETURN p.name ORDER BY p.name` returned unordered results
- `executeMatchWithRelationships()` was returning immediately without applying post-processing clauses
- Fixed by capturing result, applying ORDER BY/SKIP/LIMIT, then returning
- Affects all queries with relationship traversal: `(a)-[:TYPE]->(b)`, `(a)<-[:TYPE]-(b)`, chained patterns
- **Impact**: Fixes data integrity issues where clients relied on sorted results
- **Critical: Cartesian Product MATCH Returns Zero Rows** - Comma-separated node patterns returned empty results instead of cartesian product
- `MATCH (p:Person), (a:Area) RETURN p.name, a.code` returned 0 rows (should return N×M combinations)
- `executeMatch()` only parsed first pattern, ignoring subsequent comma-separated patterns
- Fixed by detecting multiple patterns via `splitNodePatterns()` and routing to new `executeCartesianProductMatch()`
- Now correctly generates all combinations of matched nodes
- Supports WHERE filtering, aggregation, ORDER BY, SKIP, LIMIT on cartesian results
- **Impact**: Critical for Northwind-style bulk insert patterns like `MATCH (s), (c) CREATE (p)-[:REL]->(c)`
- **Critical: Cartesian Product CREATE Only Creates One Relationship** - `MATCH` with multiple patterns followed by `CREATE` only created relationships for first match
- `MATCH (p:Person), (a:Area) CREATE (p)-[:WORKS_IN]->(a)` created 1 relationship (should create 3 for 3 persons × 1 area)
- `executeMatchCreateBlock()` was collecting only first matching node per pattern variable
- Fixed by collecting ALL matching nodes and iterating through cartesian product combinations
- Each CREATE now executes once per combination in the cartesian product
- **Impact**: Fixes bulk relationship creation patterns used in data import workflows
- **UNWIND CREATE with RETURN Returns Variable Name Instead of Values** - Return clause after `UNWIND...CREATE` returned literal variable names
- `UNWIND ['A','B','C'] AS name CREATE (n {name: name}) RETURN n.name` returned `["name","name","name"]` (should be `["A","B","C"]`)
- `replaceVariableInQuery()` failed to handle variables inside curly braces like `{name: name}`
- String splitting on spaces left `name}` which didn't match variable `name`
- Fixed by properly trimming braces `{}[]()` and preserving surrounding punctuation during replacement
- **Impact**: Fixes all UNWIND+CREATE+RETURN workflows, critical for bulk data ingestion with result tracking
- **Cartesian Product Performance** - New `executeCartesianProductMatch()` efficiently handles multi-pattern queries
- Builds combinations incrementally to avoid memory explosion on large datasets
- Supports early filtering with WHERE clause before building full product
- Properly integrates with query optimizer (ORDER BY, SKIP, LIMIT applied after filtering)
- Modified `pkg/cypher/match.go`:
- Added `executeCartesianProductMatch()` for comma-separated pattern handling
- Added `executeCartesianAggregation()` for aggregation over cartesian results
- Added `evaluateWhereForContext()` for WHERE clause evaluation on node contexts
- Fixed `executeMatch()` to detect and route multiple patterns correctly
- Fixed relationship pattern path to apply ORDER BY/SKIP/LIMIT before returning
- Modified `pkg/cypher/create.go`:
- Updated `executeMatchCreateBlock()` to collect all pattern matches (not just first)
- Added cartesian product iteration for CREATE execution
- Now creates relationships for every combination in MATCH cartesian product
- Modified `pkg/cypher/clauses.go`:
- Fixed `replaceVariableInQuery()` to handle variables in property maps `{key: value}`
- Improved punctuation preservation during variable substitution
- All existing tests pass (100% backwards compatibility)
- Fixed `TestWorksInRelationshipTypeAlternation` - ORDER BY now works correctly
- Fixed `TestUnwindWithCreate/UNWIND_CREATE_with_RETURN` - Returns actual values, not variable names
- Cartesian product patterns now pass all Northwind benchmark compatibility tests
PreviousNext