This document outlines the design principles, patterns, and architecture of LibraVDB's public API.
The API prioritizes ease of use while maintaining power and flexibility:
- Sensible defaults for common use cases
- Progressive disclosure of complexity
- Clear, self-documenting method names
Following Go best practices and conventions:
- Context-aware operations
- Error handling with detailed error types
- Interface-based design for extensibility
- Minimal dependencies
API design considers performance implications:
- Efficient memory usage patterns
- Batch operations for high throughput
- Streaming interfaces for large datasets
- Zero-copy operations where possible
Strong typing to prevent runtime errors:
- Compile-time configuration validation
- Typed metadata schemas
- Generic interfaces where appropriate
type Database interface {
CreateCollection(ctx context.Context, name string, opts ...CollectionOption) (*Collection, error)
GetCollection(name string) (*Collection, error)
ListCollections() []string
ListCollectionsWithContext(ctx context.Context) ([]string, error)
DeleteCollection(ctx context.Context, name string) error
DeleteCollections(ctx context.Context, names []string) error
Health(ctx context.Context) (*HealthStatus, error)
Stats() *DatabaseStats
Close() error
}Design Rationale:
- Database acts as the root container and factory
- Collections are created through the database for consistency
- Health and stats provide observability
- Context support for cancellation and timeouts
type Collection interface {
Insert(ctx context.Context, id string, vector []float32, metadata map[string]interface{}) error
InsertBatch(ctx context.Context, entries []VectorEntry) error
Delete(ctx context.Context, id string) error
DeleteBatch(ctx context.Context, ids []string) error
Search(ctx context.Context, vector []float32, k int) (*SearchResults, error)
Query(ctx context.Context) *QueryBuilder
Iterate(ctx context.Context, fn func(Record) error) error
ListAll(ctx context.Context) ([]Record, error)
ListByMetadata(ctx context.Context, field string, value interface{}) ([]Record, error)
Count(ctx context.Context) (int, error)
Stats() *CollectionStats
Close() error
}Design Rationale:
- Simple Insert/Search for basic operations
- Query builder for complex filtering
- Metadata as flexible map[string]interface{}
- Stats for monitoring and optimization
type QueryBuilder interface {
WithVector(vector []float32) *QueryBuilder
Eq(field string, value interface{}) *QueryBuilder
And() *QueryBuilder
Or() *QueryBuilder
Limit(limit int) *QueryBuilder
List() ([]Record, error)
Execute() (*SearchResults, error)
}Design Rationale:
- Fluent interface for readability
- Chainable methods for complex queries
- Type-safe field operations
- Deferred execution for optimization
LibraVDB uses the functional options pattern for configuration:
type Option func(*Config) error
type CollectionOption func(*CollectionConfig) error
// Usage
db, err := libravdb.New(
WithStoragePath("./data"),
WithMetrics(true),
)
collection, err := db.CreateCollection(ctx, "vectors",
WithDimension(768),
WithHNSW(32, 200, 50),
WithQuantization(quantConfig),
)Benefits:
- Backward compatibility when adding new options
- Self-documenting configuration
- Compile-time validation
- Optional parameters with defaults
All configuration is validated at creation time:
func (c *CollectionConfig) validate() error {
if c.Dimension <= 0 {
return fmt.Errorf("dimension must be positive, got %d", c.Dimension)
}
// ... more validation
return nil
}LibraVDB provides structured error information:
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Component string `json:"component"`
Context *ErrorContext `json:"context,omitempty"`
}
type ErrorCode string
const (
ErrInvalidDimension ErrorCode = "INVALID_DIMENSION"
ErrCollectionNotFound ErrorCode = "COLLECTION_NOT_FOUND"
ErrMemoryLimitExceeded ErrorCode = "MEMORY_LIMIT_EXCEEDED"
// ... more error codes
)Design Rationale:
- Machine-readable error codes
- Human-readable messages
- Component identification for debugging
- Additional context for complex errors
Built-in error recovery mechanisms:
type ErrorRecoveryManager interface {
RegisterStrategy(code ErrorCode, strategy RecoveryStrategy)
AttemptRecovery(ctx context.Context, err *Error) error
}For large-scale data ingestion:
type StreamingBatchInsert interface {
Start() error
Send(entry *VectorEntry) error
Stats() *StreamingStats
Close() error
}
// Usage
stream := collection.NewStreamingBatchInsert(opts)
stream.Start()
for _, entry := range largeDataset {
stream.Send(entry)
}Design Features:
- Backpressure handling
- Progress callbacks
- Error callbacks
- Configurable batching
For flexible data sources:
type StreamingReader interface {
Read() (*VectorEntry, error)
Close() error
}
// Usage
reader := NewChannelStreamingReader(dataChan)
stream, err := collection.StreamFromReader(reader, opts)type Collection interface {
SetMemoryLimit(bytes int64) error
GetMemoryUsage() (*MemoryUsage, error)
TriggerGC() error
EnableMemoryMapping(path string) error
}Design Rationale:
- Explicit control over memory usage
- Observability into memory consumption
- Manual GC triggering for optimization
- Memory mapping for large datasets
Built-in Prometheus metrics:
type Metrics struct {
VectorInserts prometheus.Counter
SearchQueries prometheus.Counter
SearchLatency prometheus.Histogram
MemoryUsage prometheus.Gauge
}Comprehensive health monitoring:
type HealthStatus struct {
Status string `json:"status"`
Components map[string]string `json:"components"`
Timestamp time.Time `json:"timestamp"`
}Interface-based design allows for extensions:
type Index interface {
Insert(ctx context.Context, entry *VectorEntry) error
Search(ctx context.Context, vector []float32, k int) ([]*SearchResult, error)
Size() int
Close() error
}
type Quantizer interface {
Train(ctx context.Context, vectors [][]float32) error
Encode(vector []float32) ([]byte, error)
Decode(data []byte) ([]float32, error)
}For algorithm registration:
type IndexRegistry interface {
Register(name string, factory IndexFactory)
Create(name string, config interface{}) (Index, error)
List() []string
}- Semantic versioning for releases
- Backward compatibility within major versions
- Deprecation warnings before breaking changes
type Config struct {
ExperimentalFeatures map[string]bool
}Small, focused interfaces for easier testing and mocking:
type Searcher interface {
Search(ctx context.Context, vector []float32, k int) (*SearchResults, error)
}
type Inserter interface {
Insert(ctx context.Context, id string, vector []float32, metadata map[string]interface{}) error
}Where possible, avoid unnecessary data copying:
// Efficient: reuses input slice
func (c *Collection) SearchWithVector(vector []float32) (*SearchResults, error)
// Inefficient: would copy vector
func (c *Collection) SearchWithVectorCopy(vector []float32) (*SearchResults, error)Encourage batching for better performance:
type BatchInserter interface {
InsertBatch(ctx context.Context, entries []*VectorEntry) error
}Defer expensive operations until needed:
type Collection struct {
index Index // initialized on first use
// ...
}All public interfaces have comprehensive test suites:
func TestCollectionInterface(t *testing.T) {
collection := createTestCollection(t)
// Test all interface methods
testInsert(t, collection)
testSearch(t, collection)
testQuery(t, collection)
}Interfaces allow for easy mocking:
type MockIndex struct {
insertFunc func(ctx context.Context, entry *VectorEntry) error
searchFunc func(ctx context.Context, vector []float32, k int) ([]*SearchResult, error)
}End-to-end testing of API workflows:
func TestFullWorkflow(t *testing.T) {
db := createTestDatabase(t)
collection := createTestCollection(t, db)
// Test complete workflow
insertTestData(t, collection)
searchAndValidate(t, collection)
optimizeAndValidate(t, collection)
}Every public method includes usage examples:
// Insert adds a vector to the collection.
//
// Example:
// vector := []float32{0.1, 0.2, 0.3}
// metadata := map[string]interface{}{"category": "test"}
// err := collection.Insert(ctx, "doc1", vector, metadata)
func (c *Collection) Insert(ctx context.Context, id string, vector []float32, metadata map[string]interface{}) errorClear documentation of what's stable vs experimental:
// Stable: This API is stable and will not change in backward-incompatible ways
func (c *Collection) Insert(ctx context.Context, id string, vector []float32, metadata map[string]interface{}) error
// Experimental: This API is experimental and may change
func (c *Collection) ExperimentalFeature() errorThis API design provides a solid foundation for LibraVDB that balances simplicity, performance, and extensibility while following Go best practices.