diff --git a/.github/workflows/feature-fix-workflow.yml b/.github/workflows/feature-fix-workflow.yml index 6470756..961f865 100644 --- a/.github/workflows/feature-fix-workflow.yml +++ b/.github/workflows/feature-fix-workflow.yml @@ -199,7 +199,7 @@ jobs: if [ "$ISSUE_STATE" = "OPEN" ]; then # Get recent commits for this branch - RECENT_COMMITS=$(git log "origin/develop..${{ github.ref_name }}" --pretty=format:'- %s (%h)' | head -3) + RECENT_COMMITS=$(git log "origin/develop..${{ github.ref_name }}"-n 3 --pretty=format:'- %s (%h)') gh issue comment "$ISSUE_NUM" --body "๐Ÿ”„ **Update from \`${{ github.ref_name }}\`** diff --git a/CHANGELOG_v1.5.md b/CHANGELOG_v1.5.md deleted file mode 100644 index 932afe9..0000000 --- a/CHANGELOG_v1.5.md +++ /dev/null @@ -1,225 +0,0 @@ -# Changelog v1.5 - CLI Command Parity - -## Release Date -March 6, 2026 - -## Overview -This release equalizes the CLI command set with REST API endpoints, providing feature parity between both interfaces and enhancing the user experience. - -## New Features - -### 1. STATS ALL - Detailed Statistics -**Command:** `STATS ALL` - -**Description:** Displays comprehensive statistics in JSON format including: -- MemTable records and size -- SSTable files, records, and disk usage -- WAL size -- Total record count -- Configuration parameters - -**Example:** -```bash -lsm> STATS ALL -{ - "mem_records": 3, - "mem_kb": 2, - "sst_files": 2, - "sst_records": 47, - "sst_kb": 156, - "wal_kb": 1, - "total_records": 50, - "memtable_max_size": 4 -} -``` - -**REST API Equivalent:** `GET /stats/all` - -### 2. SEARCH - Advanced Search with Prefix Support -**Command:** `SEARCH [--prefix]` - -**Description:** Searches for records matching a query with two modes: -- **Substring mode** (default): Finds all keys containing the query string -- **Prefix mode** (with `--prefix` flag): Finds all keys starting with the query string (optimized) - -**Examples:** -```bash -# Substring search -lsm> SEARCH user -โœ“ 3 registro(s) encontrado(s): - user:alice = Alice Silva - user:bob = Bob Santos - user:charlie = Charlie Costa - -# Prefix search (faster for hierarchical keys) -lsm> SEARCH user: --prefix -โœ“ 3 registro(s) encontrado(s): - user:alice = Alice Silva - user:bob = Bob Santos - user:charlie = Charlie Costa -``` - -**REST API Equivalent:** `GET /keys/search?q=query&prefix=true` - -### 3. BATCH SET - Bulk Import from Files -**Command:** `BATCH SET ` - -**Description:** Imports records from a text file in `key=value` format. - -**File Format:** -``` -# Comments start with # -user:alice=Alice Silva -user:bob=Bob Santos -product:1=Laptop Dell XPS 15 -config:theme=dark -``` - -**Features:** -- Supports comments (lines starting with `#`) -- Skips empty lines -- Automatic key/value trimming -- Error reporting with line numbers -- Progress feedback - -**Example:** -```bash -lsm> BATCH SET examples/batch_data.txt -Importando de examples/batch_data.txt... -โœ“ 23 registro(s) importado(s) -``` - -**REST API Equivalent:** `POST /keys/batch` - -### 4. SCAN Improvement -**Command:** `SCAN ` - -**Description:** Updated to use the optimized `search_prefix` method instead of showing "not implemented" warning. - -**Example:** -```bash -lsm> SCAN user: -โœ“ 3 registro(s) com prefixo 'user:': - user:alice = Alice Silva - user:bob = Bob Santos - user:charlie = Charlie Costa -``` - -## Improvements - -### Enhanced Help System -Updated `print_help()` function to include all new commands with proper formatting: -``` -STATS [ALL] - Exibe estatรญsticas (bรกsicas ou detalhadas) -SEARCH [--prefix] - Busca registros (opcionalmente por prefixo) -BATCH SET - Importa registros de arquivo -``` - -### Enhanced DEMO Command -Updated demo to showcase all new features: -- Tests SEARCH in both modes -- Displays STATS ALL output -- Demonstrates prefix scanning - -## Documentation - -### New Files -- **`docs/CLI_GUIDE.md`**: Comprehensive CLI documentation - - All command syntax and examples - - Best practices - - Troubleshooting guide - - Complete workflow examples - -- **`examples/batch_data.txt`**: Sample data file - - Demonstrates file format for BATCH SET - - Includes various data types (users, products, config) - - Comments explaining usage - -### Updated Files -- **`src/cli/mod.rs`**: Complete CLI implementation -- **`CHANGELOG_v1.5.md`**: This changelog - -## Breaking Changes -None. All existing commands remain unchanged and backward compatible. - -## Migration Guide -No migration needed. New commands are additive. - -## Implementation Details - -### Command Parsing -- Updated `splitn` to handle 4 parts for complex commands -- Maintains backward compatibility with existing command syntax - -### Error Handling -- Graceful handling of file read errors -- Line-by-line error reporting for BATCH SET -- Validates file format and provides helpful error messages - -### Performance -- SEARCH with `--prefix` flag uses optimized BTree iteration -- BATCH SET processes files line-by-line (memory efficient) -- STATS ALL uses existing engine methods (minimal overhead) - -## Testing - -### Manual Testing Completed -- [x] STATS ALL produces valid JSON -- [x] SEARCH works in both substring and prefix modes -- [x] BATCH SET imports data correctly -- [x] File format validation works properly -- [x] Error messages are helpful and accurate -- [x] SCAN command uses optimized search -- [x] All commands maintain backward compatibility - -### Test Files -- `examples/batch_data.txt` - Sample data for testing - -## CLI vs REST API Command Mapping - -| CLI Command | REST API Endpoint | Status | -|-------------|------------------|--------| -| `SET ` | `POST /keys` | โœ… Parity | -| `GET ` | `GET /keys/{key}` | โœ… Parity | -| `DELETE ` | `DELETE /keys/{key}` | โœ… Parity | -| `STATS` | `GET /stats` | โœ… Parity | -| `STATS ALL` | `GET /stats/all` | โœ… **New** | -| `SEARCH ` | `GET /keys/search?q=...` | โœ… **New** | -| `SEARCH --prefix` | `GET /keys/search?q=...&prefix=true` | โœ… **New** | -| `BATCH SET ` | `POST /keys/batch` | โœ… **New** | -| `KEYS` | `GET /keys` | โœ… Parity | -| `ALL` | `GET /scan` | โœ… Parity | -| `COUNT` | N/A (CLI only) | โœ… CLI-only | -| `SCAN ` | `GET /scan?prefix=...` | โœ… Improved | - -## Known Limitations - -1. **Token Management**: CLI does not support token management commands (low priority - better suited for REST API) -2. **Feature Flags**: CLI does not yet support feature flag management (planned for future release) -3. **Large Files**: BATCH SET loads entire file into memory (acceptable for typical use cases) - -## Future Enhancements (v1.6+) - -### Phase 2: Feature Management (Medium Priority) -- [ ] `FEATURES` - List all feature flags -- [ ] `FEATURE SET ` - Toggle feature flags - -### Phase 3: Advanced Features (Low Priority) -- [ ] `EXPORT ` - Export data to file -- [ ] `IMPORT [--format json|txt]` - Import with format detection -- [ ] `TOKEN` commands - CLI-based token management - -## Contributors -- Elio Neto (@ElioNeto) - -## Related Issues -- Closes #65 - Equalize CLI Commands with REST API Endpoints - -## References -- [CLI Guide](docs/CLI_GUIDE.md) -- [REST API Documentation](docs/API.md) -- [Configuration Guide](docs/CONFIGURATION.md) - ---- - -**Release Notes:** This release focuses on developer experience by providing CLI feature parity with the REST API. All Phase 1 (High Priority) commands have been implemented, tested, and documented. diff --git a/Cargo.lock b/Cargo.lock index f839185..97d00b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "apexstore" +name = "apex-store-rs" version = "2.1.0" dependencies = [ "actix-cors", diff --git a/Cargo.toml b/Cargo.toml index c25eea9..ff97d6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,36 @@ [package] -name = "apexstore" +name = "apex-store-rs" # Alterado para ser รบnico no crates.io version = "2.1.0" edition = "2021" authors = ["Elio Neto "] license = "MIT" repository = "https://github.com/ElioNeto/ApexStore" -description = "A high-performance LSM-tree storage engine with SSTable V2 format" +homepage = "https://github.com/ElioNeto/ApexStore" +documentation = "https://elioneto.github.io/ApexStore/" +readme = "README.md" +description = "A high-performance, embedded LSM-tree storage engine with SSTable V2 format, LZ4 compression, and Bloom Filters." +keywords = ["database", "lsm-tree", "key-value", "storage-engine", "embedded"] +categories = ["database-implementations", "data-structures"] +# Impede que arquivos de banco de dados locais sejam enviados no pacote +exclude = [ + "data/*", + "target/*", + "*.sst", + "*.log", + ".env", + "docs/assets/*" +] [[bin]] name = "apexstore-server" path = "src/bin/server.rs" [[bin]] -name = "cli" +name = "apexstore-cli" # Renomeado de 'cli' para evitar conflitos globais path = "src/bin/cli.rs" [lib] -name = "apexstore" +name = "apexstore" # Mantido para que vocรช use `use apexstore;` no seu cรณdigo path = "src/lib.rs" [features] @@ -55,6 +69,7 @@ criterion = { version = "0.5", features = ["html_reports"] } opt-level = 3 lto = true codegen-units = 1 +panic = "abort" # Opcional: reduz o tamanho do binรกrio [profile.bench] -inherits = "release" +inherits = "release" \ No newline at end of file diff --git a/README.md b/README.md index d2f58d1..7c9deaf 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,73 @@ - # ๐Ÿ—„๏ธ ApexStore +

+ ApexStore Logo +

+ +

ApexStore

+ +

+ High-performance, embedded Key-Value engine built with Rust ๐Ÿฆ€ +
+ Implementing LSM-Tree architecture with a focus on SOLID principles, observability, and performance. +

+ +

+ Documentation + License + Rust Version + Version + Docker + CI +

-[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Rust](https://img.shields.io/badge/rust-1.70%2B-orange.svg)](https://www.rust-lang.org/) -[![Version](https://img.shields.io/badge/version-1.4.0-blue.svg)](https://github.com/ElioNeto/ApexStore/releases) -[![Docker](https://img.shields.io/badge/docker-ready-blue.svg)](https://www.docker.com/) - -A high-performance, embedded key-value store written in Rust, implementing the **Log-Structured Merge-Tree (LSM-Tree)** architecture. Built with SOLID principles for production-grade reliability, testability, and maintainability. +--- ## ๐ŸŽฏ Overview -ApexStore is a modern, Rust-based storage engine designed for write-heavy workloads. It combines the durability of write-ahead logging with the efficiency of LSM-Tree architecture, providing: +ApexStore is a modern, Rust-based storage engine designed for write-heavy workloads. It combines the durability of write-ahead logging (WAL) with the efficiency of **Log-Structured Merge-Tree (LSM-Tree)** architecture. + +Built from the ground up using **SOLID principles**, it provides a production-grade storage solution that is easy to reason about, test, and maintain, while delivering the performance expected from a systems-level language. + +## โš–๏ธ Why ApexStore? + +While industry giants like RocksDB or LevelDB focus on extreme complexity, ApexStore offers: + +- **Educational Clarity**: A clean, modular implementation of LSM-Tree that serves as a blueprint for high-performance systems. +- **Strict SOLID Compliance**: Leveraging Rust's ownership model to enforce clear boundaries between MemTable, WAL, and SSTable layers. +- **Observability First**: Built-in real-time metrics for memory, disk usage, and WAL health. +- **Modern Defaults**: Native LZ4 compression, Bloom Filters, and 35+ tunable parameters via environment variables. + +## ๐Ÿ“Š Performance Benchmarks -- **High Write Throughput**: Optimized for write-intensive applications with in-memory buffering and sequential disk writes -- **Data Durability**: Write-ahead log (WAL) ensures zero data loss on crashes -- **Efficient Storage**: Block-based compression with LZ4 reduces storage footprint by 2-4x -- **Flexible Configuration**: 35+ tunable parameters via environment variablesโ€”no recompilation needed -- **Production Ready**: Comprehensive error handling, metrics, and monitoring capabilities +*Measured on AMD Ryzen 9 5900X, NVMe SSD (v1.4.0)* + +| Operation | Throughput | Visual | +|-----------|------------|--------| +| **In-Memory Writes** | ~500k ops/s | โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% | +| **Writes (with WAL)** | ~100k ops/s | โ–ˆโ–ˆโ–ˆ 20% | +| **Batch Writes** | ~1M ops/s | โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 200% | +| **MemTable Hits** | ~1.2M ops/s | โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 240% | +| **SSTable Reads** | ~50k ops/s | โ–ˆ 10% | + +> **Note:** The performance difference between In-Memory and WAL writes highlights the fsync overhead, which can be optimized via `WAL_SYNC_MODE`. ## โœจ Key Features -### Storage Engine -- **MemTable**: In-memory BTreeMap with configurable size limits for fast writes -- **Write-Ahead Log (WAL)**: ACID-compliant durability with configurable sync modes -- **SSTable V2**: Block-based storage format with: - - Sparse indexing for O(log N) lookups - - LZ4 compression for space efficiency - - Bloom filters to avoid unnecessary disk I/O - - Comprehensive metadata tracking -- **Automatic Flushing**: Seamless transition from memory to disk when thresholds are reached -- **Crash Recovery**: Automatic WAL replay on startup - -### Access Patterns -- **Interactive CLI**: REPL interface for development and debugging -- **REST API**: Full HTTP API with JSON payloads for production use -- **Batch Operations**: Efficient bulk inserts and updates -- **Search Capabilities**: Prefix and substring search (with iterator improvements coming in v2.0) - -### Advanced Features -- **Feature Flags System**: Dynamic runtime configuration with optimistic locking -- **Statistics & Monitoring**: Real-time metrics for memory, disk, and WAL usage -- **Environment-Based Config**: 35+ parameters organized by category: - - Server HTTP (12 params): networking, threading, timeouts - - LSM Engine (8 params): storage, caching, indexing - - Compaction (5 params): future-ready configuration - - Advanced Tuning (6 params): I/O, memory pools, mmap - - Monitoring (4 params): logging, metrics, telemetry +### ๐Ÿ› ๏ธ Storage Engine +- **MemTable**: In-memory BTreeMap with configurable size limits. +- **Write-Ahead Log (WAL)**: ACID-compliant durability with configurable sync modes. +- **SSTable V2**: Block-based storage with Sparse Indexing and LZ4 Compression. +- **Bloom Filters**: Drastically reduces unnecessary disk I/O for read operations. +- **Crash Recovery**: Automatic WAL replay on startup to ensure zero data loss. + +### ๐Ÿ”Œ Access Patterns +- **Interactive CLI**: REPL interface for development and debugging. +- **REST API**: Full HTTP API with JSON payloads for microservices. +- **Batch Operations**: Efficient bulk inserts and updates. +- **Search Capabilities**: Prefix and substring search (Optimized iterators coming in v2.0). ## ๐Ÿ—๏ธ Architecture -The engine follows a modular SOLID architecture where each component has a single responsibility: +The engine follows a modular architecture where each component has a single responsibility: ```mermaid graph TB @@ -95,426 +114,104 @@ graph TB style SST fill:#9cf,stroke:#333,stroke-width:2px ``` -### Data Flow: Write Path - -```mermaid -sequenceDiagram - participant Client - participant Engine - participant WAL - participant MemTable - participant Builder - participant SSTable - - Client->>Engine: put(key, value) - Engine->>WAL: append(record) - WAL-->>Engine: โœ“ persisted - Engine->>MemTable: insert(key, value) - - alt MemTable Full - Engine->>Builder: new(config, timestamp) - loop For each entry - Engine->>Builder: add(key, record) - end - Builder->>Builder: compress blocks (LZ4) - Builder->>SSTable: write(blocks + metadata + footer) - Builder-->>Engine: SSTable path - Engine->>MemTable: clear() - Engine->>WAL: truncate() - end - - Engine-->>Client: โœ“ success -``` - -### Data Flow: Read Path - -```mermaid -sequenceDiagram - participant Client - participant Engine - participant MemTable - participant SSTable - participant BloomFilter - - Client->>Engine: get(key) - Engine->>MemTable: lookup(key) - - alt Key in MemTable - MemTable-->>Engine: value - Engine-->>Client: value - else Not in MemTable - loop For each SSTable (newest first) - Engine->>BloomFilter: might_contain(key) - alt Bloom says "no" - BloomFilter-->>Engine: โœ— skip - else Bloom says "maybe" - Engine->>SSTable: binary_search_blocks(key) - alt Key found - SSTable->>SSTable: decompress_block() - SSTable-->>Engine: value - Engine-->>Client: value - end - end - end - Engine-->>Client: โœ— not found - end -``` - ## ๐Ÿš€ Quick Start ### Prerequisites +- **Rust 1.70+**: Install via [rustup.rs](https://rustup.rs/) -- **Rust 1.70+**: Install via [rustup](https://rustup.rs/) - ```bash - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - ``` - -### Installation - +### Installation & Run ```bash -# Clone the repository -git clone https://github.com/ElioNeto/ApexStore.git -cd ApexStore - -# Build the project -cargo build --release -``` +# Clone and enter +git clone https://github.com/ElioNeto/ApexStore.git && cd ApexStore -### Usage - -#### Interactive CLI Mode - -```bash -# Start the REPL +# Build and Start REPL cargo run --release # Available commands: -# > put key value -# > get key -# > delete key +# > put user:1 "John Doe" +# > get user:1 # > stats -# > help -# > exit -``` - -#### API Server Mode - -```bash -# Copy environment template (optional) -cp .env.example .env - -# Customize settings (optional) -nano .env - -# Start the server -cargo run --release --features api --bin apexstore-server ``` -The server will start at `http://0.0.0.0:8080` by default. - ## ๐Ÿณ Docker Deployment -### Quick Start with Docker Compose (Recommended) +Run ApexStore as a standalone API server: ```bash -# Clone the repository -git clone https://github.com/ElioNeto/ApexStore.git -cd ApexStore - -# Copy environment file and customize (optional) -cp .env.example .env - -# Start ApexStore +# Start with Docker Compose docker-compose up -d -# View logs -docker-compose logs -f apexstore - -# Stop the service -docker-compose down -``` - -The API will be available at `http://localhost:8080`. - -### Standalone Docker Commands - -```bash -# Build the Docker image -docker build -t apexstore:latest . - -# Run the container +# Manual run with custom config docker run -d \ --name apexstore-server \ -p 8080:8080 \ - -v apexstore-data:/data \ - -e MEMTABLE_MAX_SIZE=16777216 \ - -e BLOCK_CACHE_SIZE_MB=64 \ - apexstore:latest - -# View logs -docker logs -f apexstore-server - -# Stop the container -docker stop apexstore-server -``` - -### Docker Configuration - -You can pass environment variables to configure ApexStore: - -```bash -docker run -d \ - --name apexstore-server \ - -p 8080:8080 \ - -v apexstore-data:/data \ - -e HOST=0.0.0.0 \ - -e PORT=8080 \ -e MEMTABLE_MAX_SIZE=33554432 \ - -e BLOCK_SIZE=8192 \ - -e BLOCK_CACHE_SIZE_MB=128 \ - -e BLOOM_FALSE_POSITIVE_RATE=0.005 \ - -e API_AUTH_ENABLED=true \ - apexstore:latest -``` - -### Health Check - -Docker includes automatic health checks: - -```bash -# Check container health status -docker ps - -# Manual health check -curl http://localhost:8080/health -``` - -### Data Persistence - -ApexStore data is persisted in a Docker volume: - -```bash -# List volumes -docker volume ls | grep apexstore - -# Inspect volume -docker volume inspect apexstore-data - -# Backup data -docker run --rm -v apexstore-data:/data -v $(pwd):/backup alpine \ - tar czf /backup/apexstore-backup.tar.gz -C /data . - -# Restore data -docker run --rm -v apexstore-data:/data -v $(pwd):/backup alpine \ - tar xzf /backup/apexstore-backup.tar.gz -C /data + -v apexstore-data:/data \ + elioneto/apexstore:latest ``` -## ๐ŸŒ REST API - -### Core Operations - -| Method | Endpoint | Description | Example | -|--------|----------|-------------|----------| -| `POST` | `/keys` | Insert or update a key | `{"key": "user:1", "value": "Alice"}` | -| `GET` | `/keys/{key}` | Retrieve a value by key | `/keys/user:1` | -| `DELETE` | `/keys/{key}` | Delete a key (tombstone) | `/keys/user:1` | -| `POST` | `/keys/batch` | Batch insert/update | `[{"key": "k1", "value": "v1"}, ...]` | - -### Search & Monitoring +## ๐ŸŒ REST API Examples | Method | Endpoint | Description | |--------|----------|-------------| -| `GET` | `/keys/search/prefix?q=user:` | Prefix search | -| `GET` | `/keys/search/substring?q=alice` | Substring search | +| `POST` | `/keys` | Insert/Update: `{"key": "k1", "value": "v1"}` | +| `GET` | `/keys/{key}` | Retrieve value | | `GET` | `/stats/all` | Full telemetry (Memory, Disk, WAL) | -| `GET` | `/stats/memory` | MemTable statistics | -| `GET` | `/stats/disk` | SSTable statistics | - -### Feature Flags - -| Method | Endpoint | Description | -|--------|----------|-------------| -| `GET` | `/features` | List all feature flags | -| `POST` | `/features/{id}` | Create or update flag | `{"enabled": true}` | -| `GET` | `/features/{id}` | Get flag status | - -## โš™๏ธ Configuration - -ApexStore uses environment variables for configuration. No recompilation needed! - -### Quick Configuration Examples - -#### Stress Testing Profile -```bash -# .env -MAX_JSON_PAYLOAD_SIZE=104857600 # 100MB -MEMTABLE_MAX_SIZE=16777216 # 16MB -BLOCK_CACHE_SIZE_MB=256 -SERVER_WORKERS=16 -``` - -#### High Write Throughput -```bash -MEMTABLE_MAX_SIZE=8388608 # 8MB -COMPACTION_STRATEGY=tiered -WAL_SYNC_MODE=async_batch -BLOCK_SIZE=8192 -``` - -#### Memory Constrained -```bash -MEMTABLE_MAX_SIZE=2097152 # 2MB -BLOCK_CACHE_SIZE_MB=32 -SPARSE_INDEX_INTERVAL=32 -BLOOM_FALSE_POSITIVE_RATE=0.02 -``` - -For detailed configuration options, see [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md). ## ๐Ÿ“ Project Structure -Organized following **SOLID principles**: - ``` ApexStore/ โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ core/ # Domain logic (SRP) -โ”‚ โ”‚ โ”œโ”€โ”€ engine.rs # LSM Engine orchestration -โ”‚ โ”‚ โ”œโ”€โ”€ memtable.rs # In-memory storage -โ”‚ โ”‚ โ””โ”€โ”€ log_record.rs # Data model -โ”‚ โ”œโ”€โ”€ storage/ # Persistence (DIP) -โ”‚ โ”‚ โ”œโ”€โ”€ wal.rs # Write-Ahead Log -โ”‚ โ”‚ โ”œโ”€โ”€ sstable.rs # SSTable reader/manager -โ”‚ โ”‚ โ””โ”€โ”€ builder.rs # SSTable V2 builder -โ”‚ โ”œโ”€โ”€ infra/ # Cross-cutting concerns -โ”‚ โ”‚ โ”œโ”€โ”€ codec.rs # Serialization (Bincode) -โ”‚ โ”‚ โ”œโ”€โ”€ error.rs # Error handling -โ”‚ โ”‚ โ””โ”€โ”€ config.rs # Configuration -โ”‚ โ”œโ”€โ”€ api/ # HTTP transport (Actix-Web) -โ”‚ โ”‚ โ”œโ”€โ”€ handlers.rs # REST endpoints -โ”‚ โ”‚ โ”œโ”€โ”€ server.rs # Server setup -โ”‚ โ”‚ โ””โ”€โ”€ config.rs # Server config -โ”‚ โ”œโ”€โ”€ cli/ # Interactive interface -โ”‚ โ”‚ โ””โ”€โ”€ repl.rs # REPL implementation -โ”‚ โ””โ”€โ”€ features/ # Business domain -โ”‚ โ””โ”€โ”€ flags.rs # Feature flag management -โ”œโ”€โ”€ docs/ # Documentation -โ”‚ โ”œโ”€โ”€ CONFIGURATION.md # Configuration guide -โ”‚ โ”œโ”€โ”€ CONTRIBUTING.md # Contribution guidelines -โ”‚ โ””โ”€โ”€ SETUP.md # Development setup -โ”œโ”€โ”€ tests/ # Integration tests -โ”œโ”€โ”€ docker-compose.yml # Docker Compose configuration -โ”œโ”€โ”€ Dockerfile # Container build instructions -โ”œโ”€โ”€ .env.example # Configuration template -โ”œโ”€โ”€ Cargo.toml # Dependencies -โ”œโ”€โ”€ CHANGELOG.md # Version history -โ””โ”€โ”€ README.md # This file +โ”‚ โ”œโ”€โ”€ core/ # LSM Engine, MemTable, Domain logic +โ”‚ โ”œโ”€โ”€ storage/ # WAL, SSTable V2, Block Builder +โ”‚ โ”œโ”€โ”€ infra/ # Codec, Error Handling, Config +โ”‚ โ”œโ”€โ”€ api/ # Actix-Web Server & Handlers +โ”‚ โ””โ”€โ”€ cli/ # REPL Implementation +โ”œโ”€โ”€ docs/ # Detailed documentation & Architecture +โ”œโ”€โ”€ tests/ # Integration test suite +โ””โ”€โ”€ Dockerfile # Multi-stage build ``` -## ๐Ÿงช Testing +## ๐Ÿงช Testing & Quality ```bash -# Run all tests -cargo test - -# Run with output -cargo test -- --nocapture - -# Run specific test -cargo test test_builder_basic - -# Check code quality -cargo clippy -- -D warnings - -# Format code -cargo fmt +cargo test # Run all tests +cargo clippy -- -D warnings # Linting +cargo fmt # Formatting ``` -## ๐Ÿ“Š Performance Characteristics - -### Write Performance -- **Sequential Writes**: ~500k ops/sec (in-memory MemTable) -- **With WAL**: ~100k ops/sec (fsync overhead) -- **Batch Writes**: Up to 1M ops/sec - -### Read Performance -- **MemTable Hits**: ~1M ops/sec (BTreeMap lookup) -- **SSTable Reads**: ~50k ops/sec (with Bloom filter) -- **Cold Reads**: ~10k ops/sec (disk I/O) - -### Storage Efficiency -- **Compression Ratio**: 2-4x with LZ4 -- **Memory Overhead**: ~100 bytes per MemTable entry -- **Disk Amplification**: ~2-3x (before compaction) - -*Note: Benchmarks on AMD Ryzen 9 5900X, NVMe SSD. Your mileage may vary.* - ## ๐Ÿ—บ๏ธ Roadmap -### โœ… Completed (v1.0 - v1.4) -- [x] Core LSM engine with MemTable and WAL -- [x] SSTable V2 with sparse indexing and compression -- [x] REST API with feature flags -- [x] Comprehensive configuration system -- [x] Interactive CLI -- [x] Bloom filters for read optimization -- [x] Statistics and monitoring -- [x] Global block cache -- [x] Docker support with health checks - -### ๐Ÿšง In Progress (v1.5) -- [ ] Storage iterators for range queries -- [ ] Concurrent read optimization -- [ ] Comprehensive benchmark suite - -### ๐Ÿ”ฎ Future (v2.0+) -- [ ] Compaction strategies (Leveled, Tiered, Lazy Leveling) -- [ ] Authentication & authorization -- [ ] Data integrity validation (CRC32 checksums) -- [ ] Multi-instance support -- [ ] Secondary indexes -- [ ] Snapshot isolation -- [ ] Replication support -- [ ] Distributed consensus (Raft) - -See [`ROADMAP.md`](ROADMAP.md) for detailed timeline. +- [x] SSTable V2 with compression & Bloom Filters +- [x] REST API & Feature Flags +- [x] Global Block Cache +- [ ] **v1.5**: Storage iterators for range queries +- [ ] **v1.6**: Concurrent read optimization +- [ ] **v2.0**: Leveled/Tiered Compaction Strategies ## ๐Ÿค Contributing -Contributions are welcome! Please read our [Contributing Guidelines](docs/CONTRIBUTING.md) before submitting PRs. - -### Quick Contribution Workflow +Contributions are what make the open-source community an amazing place! Please check our [Contributing Guidelines](docs/CONTRIBUTING.md). -1. Fork the repository -2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Make your changes -4. Run tests and linter (`cargo test && cargo clippy`) -5. Commit your changes (`git commit -m 'feat: add amazing feature'`) -6. Push to your branch (`git push origin feature/amazing-feature`) -7. Open a Pull Request +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'feat: add amazing feature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request ## ๐Ÿ“„ License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. - -## ๐Ÿ™ Acknowledgments - -- **RocksDB**: Inspiration for LSM-Tree implementation -- **LevelDB**: SSTable format reference -- **Rust Community**: Amazing ecosystem and tooling +Distributed under the MIT License. See `LICENSE` for more information. ## ๐Ÿ“ง Contact -- **Author**: Elio Neto -- **Email**: netoo.elio@hotmail.com -- **GitHub**: [@ElioNeto](https://github.com/ElioNeto) -- **Project**: [ApexStore](https://github.com/ElioNeto/ApexStore) -- **Demo**: [lsm-admin-dev.up.railway.app](https://lsm-admin-dev.up.railway.app/) +**Elio Neto** - [GitHub](https://github.com/ElioNeto) - netoo.elio@hotmail.com +**Demo**: [lsm-admin-dev.up.railway.app](https://lsm-admin-dev.up.railway.app/) ## ๐ŸŒŸ Star History -If you find this project useful, please consider giving it a star! โญ +[![Star History Chart](https://api.star-history.com/svg?repos=ElioNeto/ApexStore&type=Date)](https://star-history.com/#ElioNeto/ApexStore&Date) --- - -**Built with ๐Ÿฆ€ Rust and โค๏ธ for high-performance storage systems** +

Built with ๐Ÿฆ€ Rust and โค๏ธ for high-performance storage systems

diff --git a/docs/assets/logo.png b/docs/assets/logo.png new file mode 100644 index 0000000..b540b73 Binary files /dev/null and b/docs/assets/logo.png differ diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 073186b..ac08f8d 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,37 +3,37 @@ use std::io::{self, Write}; use std::path::PathBuf; pub fn main() -> Result<(), Box> { - // Configurar tracing + // Configure tracing tracing_subscriber::fmt() .with_target(false) .with_level(true) .init(); - println!(" ___ _____ __"); - println!(" / | ____ ___ _ ______/ ___// /_____ Jl"); - println!(r" / /| | / __ \/ _ \ |/_/___/\__ \/ __/ __ \/ __/"); - println!(r" / ___ |/ /_/ / __/> < ___/ / /_/ /_/ / /_"); - println!(r"/_/ |_/ .___/\___/_/|_| /____/\__/\____/\__/"); + println!(" ___ _____ __ "); + println!(" / | ____ ___ _ __ / ___// /_____ _____ ___ "); + println!(r" / /| | / __ \/ _ \| |/_/ \__ \/ __/ __ \/ ___// _ \"); + println!(r" / ___ |/ /_/ / __/> < ___/ / /_/ /_/ / / / __/"); + println!(r"/_/ |_/ .___/\___/_/|_| /____/\__/\____/_/ \___/ "); println!(" /_/ High-Performance LSM-Tree Engine\n"); - // Configuraรงรฃo + // Configuration let config = LsmConfig::builder() .dir_path(PathBuf::from("./.lsm_data")) - .memtable_max_size(4 * 1024) // 4KB para testes + .memtable_max_size(4 * 1024) // 4KB for tests .build()?; - println!("๐Ÿ“‚ Diretรณrio de dados: {}", config.core.dir_path.display()); + println!("๐Ÿ“‚ Data directory: {}", config.core.dir_path.display()); - println!("Inicializando engine..."); + println!("Initializing engine..."); let engine = LsmEngine::new(config)?; - println!("โœ“ Engine inicializado com sucesso!\n"); + println!("โœ“ Engine initialized successfully!\n"); print_help(); println!(); // REPL Loop loop { - print!("lsm> "); + print!("ApexStore (CLI): "); io::stdout().flush()?; let mut input = String::new(); @@ -44,27 +44,28 @@ pub fn main() -> Result<(), Box> { continue; } - let parts: Vec<&str> = input.splitn(4, ' ').collect(); + let parts: Vec<&str> = input.splitn(3, ' ').collect(); let command = parts[0].to_uppercase(); match command.as_str() { "SET" => { if parts.len() < 3 { - println!("โŒ Uso: SET "); + println!("โŒ Usage: SET "); continue; } let key = parts[1].to_string(); let value = parts[2].as_bytes().to_vec(); + //let value = parts[2..].join(" ").as_bytes().to_vec(); match engine.set(key.clone(), value) { - Ok(_) => println!("โœ“ SET '{}' executado com sucesso", key), - Err(e) => println!("โŒ Erro: {}", e), + Ok(_) => println!("โœ“ SET '{}' executed successfully", key), + Err(e) => println!("โŒ Error: {}", e), } } "GET" => { if parts.len() < 2 { - println!("โŒ Uso: GET "); + println!("โŒ Usage: GET "); continue; } let key = parts[1]; @@ -74,21 +75,21 @@ pub fn main() -> Result<(), Box> { let value_str = String::from_utf8_lossy(&value); println!("โœ“ '{}' = '{}'", key, value_str); } - Ok(None) => println!("โš  Chave '{}' nรฃo encontrada", key), - Err(e) => println!("โŒ Erro: {}", e), + Ok(None) => println!("โš  Key '{}' not found", key), + Err(e) => println!("โŒ Error: {}", e), } } "DELETE" | "DEL" => { if parts.len() < 2 { - println!("โŒ Uso: DELETE "); + println!("โŒ Usage: DELETE "); continue; } let key = parts[1].to_string(); match engine.delete(key.clone()) { - Ok(_) => println!("โœ“ DELETE '{}' executado (tombstone criado)", key), - Err(e) => println!("โŒ Erro: {}", e), + Ok(_) => println!("โœ“ DELETE '{}' executed (tombstone created)", key), + Err(e) => println!("โŒ Error: {}", e), } } @@ -98,9 +99,9 @@ pub fn main() -> Result<(), Box> { match engine.stats_all() { Ok(stats) => match serde_json::to_string_pretty(&stats) { Ok(json) => println!("{}", json), - Err(e) => println!("โŒ Erro ao serializar JSON: {}", e), + Err(e) => println!("โŒ Error serializing JSON: {}", e), }, - Err(e) => println!("โŒ Erro: {}", e), + Err(e) => println!("โŒ Error: {}", e), } } else { println!("{}", engine.stats()); @@ -109,7 +110,7 @@ pub fn main() -> Result<(), Box> { "SEARCH" => { if parts.len() < 2 { - println!("โŒ Uso: SEARCH [--prefix]"); + println!("โŒ Usage: SEARCH [--prefix]"); continue; } @@ -125,16 +126,16 @@ pub fn main() -> Result<(), Box> { match results { Ok(records) => { if records.is_empty() { - println!("โš  Nenhum registro encontrado"); + println!("โš  No records found"); } else { - println!("โœ“ {} registro(s) encontrado(s):\n", records.len()); + println!("โœ“ {} record(s) found:\n", records.len()); for (key, value) in records { let value_str = String::from_utf8_lossy(&value); println!(" {} = {}", key, value_str); } } } - Err(e) => println!("โŒ Erro: {}", e), + Err(e) => println!("โŒ Error: {}", e), } } @@ -144,13 +145,16 @@ pub fn main() -> Result<(), Box> { "CLEAR" => { print!("\x1B[2J\x1B[1;1H"); // Clear screen ANSI code - println!("โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—"); - println!("โ•‘ LSM-Tree Key-Value Store - Interactive CLI โ•‘"); - println!("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"); + println!(" ___ _____ __ "); + println!(" / | ____ ___ _ __ / ___// /_____ _____ ___ "); + println!(r" / /| | / __ \/ _ \| |/_/ \__ \/ __/ __ \/ ___// _ \"); + println!(r" / ___ |/ /_/ / __/> < ___/ / /_/ /_/ / / / __/"); + println!(r"/_/ |_/ .___/\___/_/|_| /____/\__/\____/_/ \___/ "); + println!(" /_/ High-Performance LSM-Tree Engine\n"); } "EXIT" | "QUIT" | "Q" => { - println!("๐Ÿ‘‹ Encerrando LSM-Tree CLI..."); + println!("๐Ÿ‘‹ Closing ApexStore... See you later"); break; } @@ -162,7 +166,7 @@ pub fn main() -> Result<(), Box> { if parts.len() >= 3 && parts[1].to_uppercase() == "SET" { // BATCH SET let file_path = parts[2]; - println!("Importando de {}...", file_path); + println!("Importing from {}...", file_path); match std::fs::read_to_string(file_path) { Ok(content) => { @@ -182,37 +186,37 @@ pub fn main() -> Result<(), Box> { match engine.set(key.to_string(), value.as_bytes().to_vec()) { Ok(_) => count += 1, Err(e) => { - println!("โš  Erro na linha {}: {}", line_num + 1, e); + println!("โš  Error on line {}: {}", line_num + 1, e); errors += 1; } } } else { println!( - "โš  Linha {} invรกlida (formato esperado: key=value)", + "โš  Invalid line {} (expected format: key=value)", line_num + 1 ); errors += 1; } } - println!("โœ“ {} registro(s) importado(s)", count); + println!("โœ“ {} record(s) imported", count); if errors > 0 { - println!("โš  {} erro(s) encontrado(s)", errors); + println!("โš  {} error(s) found", errors); } } - Err(e) => println!("โŒ Erro ao ler arquivo: {}", e), + Err(e) => println!("โŒ Error reading file: {}", e), } } else if parts.len() >= 2 { // BATCH (existing functionality) let count: usize = match parts[1].parse() { Ok(n) => n, Err(_) => { - println!("โŒ Count invรกlido"); + println!("โŒ Invalid count"); continue; } }; - println!("Inserindo {} registros...", count); + println!("Inserting {} records...", count); let start = std::time::Instant::now(); for i in 0..count { @@ -222,16 +226,16 @@ pub fn main() -> Result<(), Box> { } let elapsed = start.elapsed(); - println!("โœ“ {} registros inseridos em {:.2?}", count, elapsed); - println!(" Taxa: {:.0} ops/s", count as f64 / elapsed.as_secs_f64()); + println!("โœ“ {} records inserted in {:.2?}", count, elapsed); + println!(" Rate: {:.0} ops/s", count as f64 / elapsed.as_secs_f64()); } else { - println!("โŒ Uso: BATCH | BATCH SET "); + println!("โŒ Usage: BATCH | BATCH SET "); } } "SCAN" => { if parts.len() < 2 { - println!("โŒ Uso: SCAN "); + println!("โŒ Usage: SCAN "); continue; } let prefix = parts[1]; @@ -240,32 +244,28 @@ pub fn main() -> Result<(), Box> { match engine.search_prefix(prefix) { Ok(records) => { if records.is_empty() { - println!("โš  Nenhum registro encontrado com prefixo '{}'", prefix); + println!("โš  No records found with prefix '{}'", prefix); } else { - println!( - "โœ“ {} registro(s) com prefixo '{}':\n", - records.len(), - prefix - ); + println!("โœ“ {} record(s) with prefix '{}':\n", records.len(), prefix); for (key, value) in records { let value_str = String::from_utf8_lossy(&value); println!(" {} = {}", key, value_str); } } } - Err(e) => println!("โŒ Erro: {}", e), + Err(e) => println!("โŒ Error: {}", e), } } "ALL" => { - println!("Listando todos os registros...\n"); + println!("Listing all records...\n"); match engine.scan() { Ok(records) => { if records.is_empty() { - println!("โš  Banco de dados vazio"); + println!("โš  Database is empty"); } else { println!("โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”"); - println!("โ”‚ Chave โ”‚ Valor โ”‚"); + println!("โ”‚ Key โ”‚ Value โ”‚"); println!("โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค"); for (key, value) in records { @@ -286,32 +286,32 @@ pub fn main() -> Result<(), Box> { println!("โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜"); } } - Err(e) => println!("โŒ Erro ao escanear: {}", e), + Err(e) => println!("โŒ Error scanning: {}", e), } } "KEYS" => match engine.keys() { Ok(keys) => { if keys.is_empty() { - println!("โš  Nenhuma chave encontrada"); + println!("โš  No keys found"); } else { - println!("Total de chaves: {}\n", keys.len()); + println!("Total keys: {}\n", keys.len()); for (i, key) in keys.iter().enumerate() { println!(" {}. {}", i + 1, key); } } } - Err(e) => println!("โŒ Erro: {}", e), + Err(e) => println!("โŒ Error: {}", e), }, "COUNT" => match engine.count() { - Ok(count) => println!("โœ“ Total de registros ativos: {}", count), - Err(e) => println!("โŒ Erro: {}", e), + Ok(count) => println!("โœ“ Total active records: {}", count), + Err(e) => println!("โŒ Error: {}", e), }, _ => { - println!("โŒ Comando desconhecido: '{}'", command); - println!(" Digite HELP para ver comandos disponรญveis"); + println!("โŒ Unknown command: '{}'", command); + println!(" Type HELP to see available commands"); } } } @@ -320,36 +320,36 @@ pub fn main() -> Result<(), Box> { } fn print_help() { - println!("Comandos disponรญveis:"); - println!(" SET - Insere ou atualiza um par chave-valor"); - println!(" GET - Recupera o valor de uma chave"); - println!(" DELETE - Remove uma chave (cria tombstone)"); - println!(" SEARCH [--prefix] - Busca registros (opcionalmente por prefixo)"); - println!(" SCAN - Lista registros com prefixo especรญfico"); - println!(" ALL - Lista todos os registros do banco"); - println!(" KEYS - Lista apenas as chaves"); - println!(" COUNT - Conta registros ativos"); - println!(" STATS [ALL] - Exibe estatรญsticas (bรกsicas ou detalhadas)"); - println!(" BATCH - Insere N registros de teste"); - println!(" BATCH SET - Importa registros de arquivo"); - println!(" DEMO - Executa demonstraรงรฃo de features"); - println!(" CLEAR - Limpa a tela"); - println!(" HELP ou ? - Exibe esta ajuda"); - println!(" EXIT, QUIT ou Q - Sai do programa"); + println!("Available commands:"); + println!(" SET - Insert or update a key-value pair"); + println!(" GET - Retrieve the value of a key"); + println!(" DELETE - Remove a key (creates tombstone)"); + println!(" SEARCH [--prefix] - Search records (optionally by prefix)"); + println!(" SCAN - List records with specific prefix"); + println!(" ALL - List all database records"); + println!(" KEYS - List only the keys"); + println!(" COUNT - Count active records"); + println!(" STATS [ALL] - Display statistics (basic or detailed)"); + println!(" BATCH - Insert N test records"); + println!(" BATCH SET - Import records from file"); + println!(" DEMO - Run feature demonstration"); + println!(" CLEAR - Clear the screen"); + println!(" HELP or ? - Display this help"); + println!(" EXIT, QUIT or Q - Exit the program"); } fn run_demo(engine: &LsmEngine) -> Result<(), Box> { println!("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—"); - println!("โ•‘ DEMO AUTOMรTICA โ•‘"); + println!("โ•‘ AUTOMATIC DEMO โ•‘"); println!("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"); - println!("1. Inserindo dados de exemplo..."); + println!("1. Inserting sample data..."); engine.set("user:alice".to_string(), b"Alice Silva".to_vec())?; engine.set("user:bob".to_string(), b"Bob Santos".to_vec())?; engine.set("user:charlie".to_string(), b"Charlie Costa".to_vec())?; - println!(" โœ“ 3 usuรกrios inseridos\n"); + println!(" โœ“ 3 users inserted\n"); - println!("2. Lendo dados..."); + println!("2. Reading data..."); if let Some(v) = engine.get("user:alice")? { println!(" user:alice = {}", String::from_utf8_lossy(&v)); } @@ -358,66 +358,59 @@ fn run_demo(engine: &LsmEngine) -> Result<(), Box> { } println!(); - println!("3. Atualizando user:alice..."); + println!("3. Updating user:alice..."); engine.set("user:alice".to_string(), b"Alice Silva Santos".to_vec())?; if let Some(v) = engine.get("user:alice")? { - println!( - " user:alice = {} (atualizado)", - String::from_utf8_lossy(&v) - ); + println!(" user:alice = {} (updated)", String::from_utf8_lossy(&v)); } println!(); - println!("4. Deletando user:bob..."); + println!("4. Deleting user:bob..."); engine.delete("user:bob".to_string())?; match engine.get("user:bob")? { - Some(_) => println!(" โŒ Erro: ainda existe"), - None => println!(" โœ“ user:bob deletado com sucesso"), + Some(_) => println!(" โŒ Error: still exists"), + None => println!(" โœ“ user:bob deleted successfully"), } println!(); - println!("5. Forรงando mรบltiplas escritas para flush..."); + println!("5. Forcing multiple writes to trigger flush..."); for i in 0..10 { engine.set( format!("product:{}", i), - format!( - "Product {} - Descriรงรฃo longa para forรงar flush automรกtico", - i - ) - .into_bytes(), + format!("Product {} - Long description to force automatic flush", i).into_bytes(), )?; } - println!(" โœ“ 10 produtos inseridos\n"); + println!(" โœ“ 10 products inserted\n"); - println!("6. Testando novos comandos..."); + println!("6. Testing new commands..."); println!(" - SEARCH user:"); match engine.search("user:") { - Ok(results) => println!(" Encontrados {} registros", results.len()), - Err(e) => println!(" Erro: {}", e), + Ok(results) => println!(" Found {} records", results.len()), + Err(e) => println!(" Error: {}", e), } println!(" - SEARCH user: --prefix"); match engine.search_prefix("user:") { - Ok(results) => println!(" Encontrados {} registros", results.len()), - Err(e) => println!(" Erro: {}", e), + Ok(results) => println!(" Found {} records", results.len()), + Err(e) => println!(" Error: {}", e), } println!(); - println!("7. Estatรญsticas finais (bรกsicas):"); + println!("7. Final statistics (basic):"); println!("{}", engine.stats()); - println!("\n8. Estatรญsticas detalhadas:"); + println!("\n8. Detailed statistics:"); match engine.stats_all() { Ok(stats) => { if let Ok(json) = serde_json::to_string_pretty(&stats) { println!("{}", json); } } - Err(e) => println!(" Erro: {}", e), + Err(e) => println!(" Error: {}", e), } println!("\nโ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—"); - println!("โ•‘ DEMO CONCLUรDA โ•‘"); + println!("โ•‘ DEMO COMPLETED โ•‘"); println!("โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n"); Ok(())