Skip to main content

The Database Performance Problem

Traditional database operations in PHP involve multiple data copies:
Each arrow represents a memory copy:
  1. Database sends result over network
  2. Native driver allocates memory for result
  3. PDO copies data into PHP-compatible structures
  4. Your code converts to PHP arrays
  5. json_encode() serializes for API response
For a 10MB result set, traditional PHP may allocate 50MB+ of memory across all these copies.

Zero-Copy Architecture

Lyger’s Zero-Copy Database keeps data in Rust memory and only copies once:
  1. Database sends result to Rust
  2. Rust stores in native memory structures
  3. Rust returns pointer (memory address) to PHP
  4. When needed, Rust serializes directly to JSON
  5. PHP receives final JSON (one copy)
The result set stays in Rust’s memory until you’re ready to use it. No intermediate copies.

How It Works

Query Execution

The lyger_db_query FFI function:
  • Connects to the database (or reuses connection)
  • Executes the SQL query
  • Stores results in Rust’s heap memory
  • Returns the memory address as an integer
The return value is a memory pointer disguised as an integer. PHP doesn’t touch the actual data.

Result Serialization

The lyger_jsonify_result FFI function:
  • Receives the pointer to the result set
  • Iterates through rows in Rust (fast!)
  • Serializes directly to JSON string
  • Returns JSON to PHP

Memory Cleanup

Always free result sets when done! Failing to call freeResult() causes memory leaks in the Rust process.

High-Level API

The dbQueryJson method combines all three operations:

Usage Examples

Basic Query

Manual Memory Management

For advanced use cases, manage the pointer lifecycle manually:
In some scenarios, you may want to defer serialization:
  • Execute multiple queries concurrently
  • Perform calculations before fetching results
  • Reduce memory pressure by serializing only when needed
The result set stays in Rust’s memory (which is more efficient) until you explicitly serialize it.

Database Support

Lyger’s Zero-Copy Database supports multiple database engines through their Rust drivers:
The Rust driver is determined by the DSN prefix. All drivers support the same zero-copy architecture.

Connection Examples

Performance Comparison

Memory Usage

Let’s query 10,000 user records (~1MB result set):

Speed Benchmarks

Query: SELECT * FROM users LIMIT 10000
Actual performance depends on database, network latency, and hardware. Zero-Copy shines with large result sets.

Integration with QueryBuilder

While the Engine class provides low-level access, you’ll typically use the QueryBuilder:
Currently, QueryBuilder uses PDO internally. Zero-Copy database is used by Cache and internal systems. Full QueryBuilder integration is planned for future releases.

Advanced: Streaming Results

For extremely large result sets, you can stream data:
Streaming API is not yet implemented in v0.1. Currently, you must fetch the entire result set at once.

Connection Pooling

The Rust layer maintains connection pools for better performance:
Connection pooling benefits:
  • No connection overhead for subsequent queries
  • Connections kept alive across requests (Always-Alive server)
  • Automatic reconnection on failure
  • Configurable pool size and timeouts
In Always-Alive mode, database connections persist across HTTP requests, making subsequent queries even faster.

Error Handling

  • Pointer is 0: Query execution failed (syntax error, connection issue, etc.)
  • Invalid JSON: Result serialization failed (encoding issue, memory corruption)
  • Exception during processing: Network timeout, connection lost, out of memory
Always use try-finally to ensure cleanup!

Best Practices

1. Always Free Results

2. Check for Errors

3. Use Prepared Statements

The current Zero-Copy API doesn’t support parameter binding. Always sanitize user input before building queries, or use the QueryBuilder for safe queries.

4. Limit Result Size

Troubleshooting

Query Returns Empty Array

Possible causes:
  • Table is empty
  • Query syntax error
  • Connection failed
  • FFI not available
Check the pointer:

Memory Leaks

If memory grows over time:
  • Ensure you call freeResult() for every query
  • Use dbQueryJson() which handles cleanup automatically
  • Check for stored pointers in class properties

Future Improvements

Planned features for future releases:
  • Parameter binding - Safe query parameters
  • Streaming results - Process huge datasets chunk-by-chunk
  • Query result objects - Type-safe result handling
  • Async queries - Non-blocking database operations
  • QueryBuilder integration - Full zero-copy support in QueryBuilder

Next Steps

Architecture Overview

Understand the complete architecture

Rust FFI Integration

Learn about FFI and memory management

Query Builder

Use the fluent query builder

Models

Work with Eloquent-style models