HeliosDB Nano Phase 3 User Guide
HeliosDB Nano Phase 3 User Guide
Version: v2.0.0-beta Last Updated: November 18, 2025 Status: Production Ready
Table of Contents
- Introduction
- Product Quantization for Vectors
- Quantized HNSW Index
- Database Branching
- Time-Travel Queries
- Materialized Views
- System Views
- Performance Tuning
- Best Practices
- Troubleshooting
Introduction
Phase 3 brings enterprise-grade features to HeliosDB Nano:
- 8-16x vector compression with Product Quantization
- Git-like database branching for zero-downtime workflows
- Time-travel queries for point-in-time data access
- Intelligent materialized views with auto-refresh
What’s New in v2.0
| Feature | Status | Description |
|---|---|---|
| Product Quantization | ✅ Production | 8-16x vector memory compression |
| Quantized HNSW | ✅ Production | Memory-efficient vector search |
| SQL Branching | 🔧 Beta | CREATE/DROP/MERGE BRANCH syntax |
| Time-Travel | 🔧 Beta | AS OF TIMESTAMP/TRANSACTION queries |
| Materialized Views | 🔧 Beta | Auto-refresh with CPU limits |
Product Quantization
Overview
Product Quantization (PQ) compresses high-dimensional vectors by 8-16x with minimal accuracy loss (95-98% recall@10).
How It Works:
- Split vector into M sub-vectors
- Quantize each sub-vector independently
- Store codes instead of full values
Memory Savings:
- 768-dim vector: 3,072 bytes → 8 bytes (384x compression)
- 512-dim vector: 2,048 bytes → 8 bytes (256x compression)
- 256-dim vector: 1,024 bytes → 4 bytes (256x compression)
Quick Start
use heliosdb_nano::vector::quantization::{ ProductQuantizer, ProductQuantizerConfig,};
// 1. Configure PQ for your vector dimensionlet config = ProductQuantizerConfig::default_for_dimension(768)?;
// 2. Train on your data (10K+ vectors recommended)let pq = ProductQuantizer::train(config, &training_vectors)?;
// 3. Encode vectors for storagelet quantized = pq.encode(&vector)?;println!("Compressed from 3KB to {} bytes!", quantized.memory_size());
// 4. Search efficientlylet query = &my_query_vector;let distance_table = pq.precompute_distance_table(query)?;let distance = pq.compute_distance_with_table(&distance_table, &quantized)?;Configuration Options
pub struct ProductQuantizerConfig { /// Number of sub-quantizers (M) /// Default: 8 for 768-dim, 4 for 256-dim pub num_subquantizers: usize,
/// Centroids per sub-quantizer (K) /// Default: 256 (fits in u8) pub num_centroids: usize,
/// Vector dimension (D) /// Must be divisible by num_subquantizers pub dimension: usize,
/// K-means iterations for training /// Default: 25 pub training_iterations: usize,
/// Minimum training samples /// Default: 10,000 pub min_training_samples: usize,}Performance Tips
Training:
- Use 10,000+ vectors for best quality
- More iterations = better codebook (diminishing returns after 25)
- Training is one-time cost, amortize over all searches
Search:
- Always use
precompute_distance_table()for batch queries - Precompute once, search many times (100-1000x faster)
- For single query, direct
compute_distance()is fine
Memory:
- Codebook size: M × K × (D/M) × 4 bytes
- Example (768-dim): 8 × 256 × 96 × 4 = 768KB
- Per-vector: M bytes
- Example (M=8): 8 bytes
Quantized HNSW Index
Overview
Combines HNSW graph structure with Product Quantization for memory-efficient approximate nearest neighbor search.
Benefits:
- 8-16x memory reduction vs standard HNSW
- 95-98% search accuracy maintained
- Fast insertion and search
Quick Start
use heliosdb_nano::vector::{ QuantizedHnswIndex, QuantizedHnswConfig,};
// 1. Configure indexlet config = QuantizedHnswConfig::default_for_dimension(768)?;
// 2. Train index (trains PQ internally)let index = QuantizedHnswIndex::train(config, &training_vectors)?;
// 3. Insert vectorsfor (id, vector) in vectors.iter().enumerate() { index.insert(id as u64, vector)?;}
// 4. Search for nearest neighborslet results = index.search(&query, 10)?; // Top 10for (id, distance) in results { println!("Vector {} is {} away", id, distance);}
// 5. Check memory savingslet stats = index.memory_stats();println!("Compression: {:.1}x", stats.compression_ratio);println!("{}", stats.format());Configuration Options
pub struct QuantizedHnswConfig { /// HNSW graph connectivity pub max_connections: usize, // Default: 16
/// Build quality (larger = better accuracy) pub ef_construction: usize, // Default: 200
/// Search quality (larger = better recall) pub ef_search: usize, // Default: 200
/// Vector dimension pub dimension: usize,
/// Distance metric (L2, Cosine, etc.) pub distance_metric: DistanceMetric,
/// Product Quantization config pub pq_config: ProductQuantizerConfig,
/// Use PQ for storage (vs keeping originals) pub use_pq_storage: bool, // Default: true}Memory Usage Example
Scenario: 100,000 vectors (768-dim)
Without PQ (standard HNSW):- Vectors: 100K × 768 × 4 = 307 MB- Graph: ~50 MB (estimate)- Total: ~357 MB
With PQ (Quantized HNSW):- Quantized vectors: 100K × 8 = 800 KB- Codebook: 768 KB- Graph: ~50 MB- Total: ~51.6 MB
Compression: 6.9xDatabase Branching
Overview
Create isolated database branches for testing, development, or rollback scenarios.
SQL Syntax
Create Branch
-- Create branch from current stateCREATE DATABASE BRANCH staging FROM CURRENT AS OF NOW;
-- Create branch from specific point in timeCREATE DATABASE BRANCH test FROM main AS OF TIMESTAMP '2025-11-15 06:00:00';
-- Create branch from transactionCREATE DATABASE BRANCH backup FROM CURRENT AS OF TRANSACTION 987654;
-- With optionsCREATE DATABASE BRANCH prod_copy FROM main AS OF NOWWITH ( replication_factor = 3, region = 'us-west-2');Drop Branch
-- Drop branch (fails if doesn't exist)DROP DATABASE BRANCH staging;
-- Drop if existsDROP DATABASE BRANCH IF EXISTS staging;Merge Branch
-- Merge with conflict resolutionMERGE DATABASE BRANCH staging INTO mainWITH ( conflict_resolution = 'branch_wins', delete_branch_after = true);
-- Conflict resolution options:-- 'branch_wins' - Source branch changes win-- 'target_wins' - Target branch changes win-- 'fail' - Fail on any conflictUse Cases
Development Workflow:
-- 1. Create dev branchCREATE DATABASE BRANCH dev FROM main AS OF NOW;
-- 2. Make changes in dev branch-- ... development work ...
-- 3. Test thoroughly-- ... testing ...
-- 4. Merge back to mainMERGE DATABASE BRANCH dev INTO mainWITH (conflict_resolution = 'branch_wins');
-- 5. Clean upDROP DATABASE BRANCH dev;Blue-Green Deployment:
-- 1. Create green deploymentCREATE DATABASE BRANCH green FROM blue AS OF NOW;
-- 2. Deploy new version to green-- ... deployment ...
-- 3. Switch traffic to green (application-level)
-- 4. If success, make green the new blueMERGE DATABASE BRANCH green INTO blue;
-- 5. If rollback needed, just switch back to blueTime-Travel Queries
Overview
Query historical data as it existed at a specific point in time.
SQL Syntax
AS OF TIMESTAMP
-- Query data as of specific timestampSELECT * FROM ordersAS OF TIMESTAMP '2025-11-15 06:00:00'WHERE user_id = 123;
-- Compare current vs historicalSELECT current.total as current_total, historical.total as yesterday_totalFROM orders currentJOIN orders AS OF TIMESTAMP '2025-11-17 00:00:00' historical ON current.order_id = historical.order_id;AS OF TRANSACTION
-- Query as of transaction IDSELECT * FROM ordersAS OF TRANSACTION 987654WHERE status = 'pending';AS OF SCN
-- Query as of System Change NumberSELECT * FROM ordersAS OF SCN 123456789;Use Cases
Audit and Compliance:
-- Show what user saw at time of complaintSELECT * FROM product_catalogAS OF TIMESTAMP '2025-11-15 14:23:00'WHERE product_id = 'WIDGET-123';Data Recovery:
-- Find accidentally deleted rowsSELECT * FROM ordersAS OF TIMESTAMP '2025-11-18 00:00:00'WHERE order_id NOT IN (SELECT order_id FROM orders);Debugging:
-- Compare data before/after bugSELECT before.*, after.*FROM orders AS OF TIMESTAMP '2025-11-17 23:59:59' beforeFULL OUTER JOIN orders after USING (order_id)WHERE before.total != after.total;Materialized Views
Overview
Precomputed query results that can auto-refresh with CPU awareness.
SQL Syntax
Create Materialized View
CREATE MATERIALIZED VIEW user_stats ASSELECT user_id, COUNT(*) as order_count, SUM(total) as total_revenue, AVG(total) as avg_order_valueFROM ordersGROUP BY user_idWITH ( auto_refresh = true, -- Enable auto-refresh max_cpu_percent = 15, -- CPU limit (0-100) threshold_table_size = '1GB', -- Min base table size threshold_dml_rate = 100, -- DMLs/sec trigger lazy_update = true, -- Defer during high load lazy_catchup_window = '1 hour' -- Max staleness allowed);Refresh Materialized View
-- Manual refreshREFRESH MATERIALIZED VIEW user_stats;
-- Concurrent refresh (non-blocking)REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats;Drop Materialized View
-- Drop viewDROP MATERIALIZED VIEW user_stats;
-- Drop if existsDROP MATERIALIZED VIEW IF EXISTS user_stats;Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
auto_refresh | bool | false | Enable background auto-refresh |
max_cpu_percent | float | 15.0 | Maximum CPU usage (0-100) |
threshold_table_size | string | ’0’ | Minimum base table size for refresh |
threshold_dml_rate | int | 0 | DMLs/sec to trigger refresh |
lazy_update | bool | false | Defer refresh during high load |
lazy_catchup_window | string | ’1h’ | Maximum staleness allowed |
Use Cases
Analytics Dashboard:
CREATE MATERIALIZED VIEW daily_metrics ASSELECT DATE(created_at) as date, COUNT(*) as orders, SUM(total) as revenueFROM ordersGROUP BY DATE(created_at)WITH ( auto_refresh = true, max_cpu_percent = 10, threshold_dml_rate = 50);Real-Time Aggregations:
CREATE MATERIALIZED VIEW product_popularity ASSELECT product_id, COUNT(*) as view_count, COUNT(DISTINCT user_id) as unique_viewersFROM product_viewsGROUP BY product_idWITH ( auto_refresh = true, max_cpu_percent = 20, lazy_catchup_window = '5 minutes');System Views
Overview
Query system metadata and statistics.
Available System Views
pg_database_branches()
-- List all branchesSELECT * FROM pg_database_branches();
-- Output columns:-- branch_name, parent_branch, created_at, size_bytespg_compare_branches()
-- Compare two branchesSELECT * FROM pg_compare_branches('main', 'staging');
-- Output columns:-- table_name, rows_in_main, rows_in_staging, differencepg_mv_staleness()
-- Check materialized view freshnessSELECT * FROM pg_mv_staleness();
-- Output columns:-- view_name, last_refresh, staleness_sec, auto_refreshpg_mv_cpu_usage()
-- Monitor MV CPU usageSELECT * FROM pg_mv_cpu_usage();
-- Output columns:-- view_name, max_cpu_percent, current_cpu_percent, statuspg_vector_index_stats()
-- Vector index statisticsSELECT * FROM pg_vector_index_stats();
-- Output columns:-- index_name, num_vectors, memory_bytes, compression_ratioPerformance Tuning
Product Quantization Tuning
Accuracy vs Compression Tradeoff:
// Higher accuracy, less compressionlet config = ProductQuantizerConfig { num_subquantizers: 4, // Fewer = better accuracy num_centroids: 256, dimension: 768, ..Default::default()};
// Higher compression, lower accuracylet config = ProductQuantizerConfig { num_subquantizers: 16, // More = better compression num_centroids: 256, dimension: 768, ..Default::default()};Training Quality:
- More training samples = better codebook (10K minimum, 100K+ ideal)
- More iterations = better convergence (25 recommended, 50 for critical applications)
HNSW Tuning
Build Quality:
let config = QuantizedHnswConfig { ef_construction: 200, // Default // For better accuracy: 400-800 // For faster build: 100-150 ..Default::default()};Search Quality:
let config = QuantizedHnswConfig { ef_search: 200, // Default // For better recall: 400-1000 // For faster search: 50-100 ..Default::default()};Materialized View Tuning
CPU Budget:
-- Conservative (low impact)WITH (max_cpu_percent = 10)
-- BalancedWITH (max_cpu_percent = 15)
-- Aggressive (faster refresh)WITH (max_cpu_percent = 30)Refresh Triggers:
-- Refresh frequentlyWITH ( threshold_dml_rate = 10, -- Low threshold lazy_catchup_window = '1 minute' -- Short window)
-- Refresh infrequentlyWITH ( threshold_dml_rate = 1000, -- High threshold lazy_catchup_window = '1 hour' -- Long window)Best Practices
Vector Workloads
-
Train on Representative Data
- Use production-like vectors for training
- Include edge cases and outliers
- Retrain periodically as data distribution changes
-
Batch Operations
- Always use
precompute_distance_table()for multiple searches - Encode vectors in batches for better throughput
- Amortize training cost over many vectors
- Always use
-
Monitor Accuracy
- Periodically check recall@k on validation set
- Alert if accuracy drops below threshold
- Consider retraining if drift detected
Database Branching
-
Branch Naming Convention
<purpose>-<timestamp>Examples:- dev-20251118- staging-v2.0- backup-before-migration -
Branch Lifecycle
- Create → Work → Test → Merge → Delete
- Don’t accumulate stale branches
- Document branch purpose in metadata
-
Merge Conflicts
- Use
branch_winsfor development → production - Use
target_winsfor rollbacks - Use
failfor manual conflict resolution
- Use
Materialized Views
-
Start Conservative
-- Begin with safe settingsWITH (max_cpu_percent = 10,lazy_update = true)-- Gradually increase if needed -
Monitor Performance
-- Check regularlySELECT * FROM pg_mv_staleness();SELECT * FROM pg_mv_cpu_usage(); -
Index the MV
-- Create indexes on frequently queried columnsCREATE INDEX idx_user_stats_revenueON user_stats(total_revenue DESC);
Troubleshooting
Product Quantization Issues
Problem: Low search accuracy (recall <90%)
Solutions:
- Use more training samples (100K+ recommended)
- Reduce number of sub-quantizers (try M=4 instead of M=8)
- Increase training iterations (try 50 instead of 25)
- Check training data quality (outliers, distribution)
Problem: Slow training
Solutions:
- Reduce training set size (10K is minimum)
- Reduce training iterations
- Use smaller num_centroids (128 instead of 256)
Problem: Out of memory during training
Solutions:
- Train in batches
- Reduce num_centroids
- Use dimensionality reduction first
HNSW Issues
Problem: Slow insertion
Solutions:
- Reduce ef_construction
- Increase max_connections (paradoxically can be faster)
- Batch insertions
Problem: Low search recall
Solutions:
- Increase ef_search
- Retrain with more representative data
- Check PQ configuration
Materialized View Issues
Problem: MV is stale
Solutions:
-- Check stalenessSELECT * FROM pg_mv_staleness()WHERE view_name = 'my_view';
-- Manual refresh if neededREFRESH MATERIALIZED VIEW my_view;
-- Adjust auto-refresh settingsALTER MATERIALIZED VIEW my_viewSET (threshold_dml_rate = 50); -- Lower thresholdProblem: High CPU usage
Solutions:
-- Lower CPU budgetALTER MATERIALIZED VIEW my_viewSET (max_cpu_percent = 10);
-- Enable lazy updatesALTER MATERIALIZED VIEW my_viewSET (lazy_update = true);Migration Guide
From v1.x to v2.0
Vector Indexes:
-- Old (v1.x): Standard HNSWCREATE INDEX vec_idx ON docs USING hnsw (embedding);
-- New (v2.0): Quantized HNSWCREATE INDEX vec_idx ON docsUSING hnsw (embedding vector_cosine_ops)WITH (quantization = 'product', pq_subquantizers = 8);No Breaking Changes:
- All v1.x queries continue to work
- Vector indexes auto-upgraded to use PQ
- Existing data preserved
Getting Help
Documentation:
- Full API docs:
docs/api/ - Examples:
examples/ - Benchmarks:
benches/
Support:
- GitHub Issues: https://github.com/dimensigon/HDB-HeliosDB-Nano/issues
- Community: [Discord/Slack link]
- Email: support@heliosdb.com
Contributing:
- Contribution guide:
CONTRIBUTING.md - Code of conduct:
CODE_OF_CONDUCT.md
User Guide Version: 1.0 Last Updated: November 18, 2025 Applies to: HeliosDB Nano v2.0.0-beta and later