geode-client 0.1.1-alpha.20

Rust client library for Geode graph database with full GQL support
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# Geode Rust Client

A high-performance async Rust client library for [Geode](https://gitlab.com/devnw/codepros/geode/geode) graph database with full GQL (ISO/IEC 39075:2024) support.

## Features

- 🚀 **Fully async** using tokio with type safety
- 🔒 **QUIC + TLS 1.3** for secure, high-performance networking
- 🌐 **gRPC transport** option using tonic (alternative to QUIC)
- 📦 **Protobuf wire protocol** for efficient serialization
- 📝 **Full GQL support** (98.6% ISO compliance)
- 🏗️ **Query builders** for programmatic query construction
- 🔐 **Complete authentication** with RBAC and RLS support
- 🏊 **Connection pooling** for concurrent workloads
- 📊 **Rich type system** with Decimal, temporal types
- 🎯 **Type-safe** with Rust's strong type system and zero-cost abstractions

## Installation

Install from [crates.io](https://crates.io/crates/geode-client):

```bash
cargo add geode-client tokio --features tokio/full
```

Or add to your `Cargo.toml`:

```toml
[dependencies]
geode-client = "0.1.1-alpha.8"
tokio = { version = "1", features = ["full"] }
```

## Requirements

- Rust 1.85+
- tokio runtime
- Running Geode server

### Transport Options

| Transport | Library | Default Port | Use Case |
|-----------|---------|--------------|----------|
| QUIC | quinn 0.11 | 3141 | High-performance, low-latency |
| gRPC | tonic 0.12 | 50051 | Broad compatibility, HTTP/2 |

## Quick Start

### Using DSN (Recommended)

```rust
use geode_client::{Client, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // QUIC transport (default, high-performance)
    let client = Client::from_dsn("quic://127.0.0.1:3141?insecure_tls_skip_verify=true")?;

    // Or gRPC transport (HTTP/2 based)
    // let client = Client::from_dsn("grpc://127.0.0.1:50051?tls=0")?;

    let mut conn = client.connect().await?;

    let (page, _) = conn.query("RETURN 1 AS x, 'Hello Geode' AS greeting").await?;

    for row in &page.rows {
        let x = row.get("x").unwrap().as_int()?;
        let greeting = row.get("greeting").unwrap().as_string()?;
        println!("x={}, greeting={}", x, greeting);
    }

    conn.close()?;
    Ok(())
}
```

### Basic QUIC Connection (Builder API)

```rust
use geode_client::{Client, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create QUIC client using builder pattern
    let client = Client::new("127.0.0.1", 3141)
        .skip_verify(true)
        .page_size(1000);

    let mut conn = client.connect().await?;
    println!("Connected to Geode via QUIC!");

    let (page, _) = conn.query("RETURN 1 AS x").await?;

    for row in &page.rows {
        let x = row.get("x").unwrap().as_int()?;
        println!("x={}", x);
    }

    conn.close()?;
    Ok(())
}
```

### Query with Parameters

```rust
use std::collections::HashMap;

let mut params = HashMap::new();
params.insert("name".to_string(), serde_json::json!("Alice"));
params.insert("age".to_string(), serde_json::json!(30));

let page = conn.query(
    "MATCH (p:Person {name: $name}) RETURN p.age AS age",
    Some(params)
).await?;
```

### Using Query Builder

```rust
use geode_client::QueryBuilder;

let (query, params) = QueryBuilder::new()
    .match_pattern("(p:Person {name: $name})-[:KNOWS]->(friend:Person)")
    .where_clause("friend.age > 25")
    .return_(&["friend.name AS name", "friend.age AS age"])
    .order_by(&["friend.age DESC"])
    .limit(10)
    .with_param("name", "Alice")
    .build();

let page = conn.query(&query, Some(params)).await?;
```

### Transactions

```rust
conn.begin().await?;

match conn.query("CREATE (p:Person {name: $name})", params).await {
    Ok(_) => conn.commit().await?,
    Err(e) => {
        conn.rollback().await?;
        return Err(e);
    }
}
```

### Savepoints (Partial Rollback)

```rust
conn.begin().await?;

// Create initial data
conn.query("CREATE (p:Person {name: 'Alice', age: 30})").await?;

// Create a savepoint
let sp = conn.savepoint("before_update")?;

// Make changes
conn.query("MATCH (p:Person {name: 'Alice'}) SET p.age = 40").await?;

// Rollback to savepoint (undoes the age change)
conn.rollback_to(&sp).await?;

// Alice's age is still 30
conn.commit().await?;
```

### Connection Pooling

```rust
use geode_client::ConnectionPool;

// Create connection pool
let pool = ConnectionPool::new("127.0.0.1", 3141, 10)
    .skip_verify(true)
    .page_size(1000);

// Acquire connection from pool
let conn = pool.acquire().await?;
let page = conn.query("RETURN 1", None).await?;

// Connection automatically returns to pool when dropped
println!("Pool size: {}", pool.size().await);
```

### Prepared Statements

```rust
use geode_client::PreparedStatement;

// Create a prepared statement
let stmt = conn.prepare("MATCH (p:Person {id: $id}) RETURN p.name, p.age")?;

// Execute multiple times with different parameters
for id in 1..=100 {
    let mut params = HashMap::new();
    params.insert("id".to_string(), Value::int(id));
    let (page, _) = stmt.execute(&mut conn, &params).await?;
    // Process results...
}
```

### Query Explain and Profile

```rust
// Get the execution plan without running the query
let plan = conn.explain("MATCH (p:Person)-[:KNOWS]->(f) RETURN f").await?;
println!("Estimated rows: {}", plan.estimated_rows);
for op in &plan.operations {
    println!("  {} - {}", op.op_type, op.description);
}

// Execute with profiling to get actual timing
let profile = conn.profile("MATCH (p:Person) RETURN p LIMIT 100").await?;
println!("Execution time: {:.2}ms", profile.execution_time_ms);
println!("Actual rows: {}", profile.actual_rows);
```

### Batch Queries

```rust
// Execute multiple queries efficiently
let results = conn.batch(&[
    ("MATCH (n:Person) RETURN count(n)", None),
    ("MATCH (n:Company) RETURN count(n)", None),
    ("MATCH ()-[r:WORKS_AT]->() RETURN count(r)", None),
]).await?;

for (i, page) in results.iter().enumerate() {
    println!("Query {}: {} rows", i + 1, page.rows.len());
}
```

## Connection Configuration

The client uses a builder pattern for configuration:

```rust
let client = Client::new("127.0.0.1", 3141)  // host and port
    .skip_verify(true)                        // Skip TLS verification (development only)
    .page_size(1000)                          // Results page size
    .client_name("my-app")                    // Client name for server logs
    .client_version("1.0.0")                  // Client version
    .conformance("min")                       // GQL conformance level
    .username("admin")                        // Authentication username
    .password("secret");                      // Authentication password
```

### DSN Connection String

> **Note**: See [`geode/docs/DSN.md`]../geode/docs/DSN.md for the complete DSN specification.

You can also create a client from a DSN (Data Source Name) string:

```rust
use geode_client::Client;

// QUIC transport (recommended for performance)
let client = Client::from_dsn("quic://localhost:3141?insecure_tls_skip_verify=true").unwrap();

// gRPC transport (HTTP/2 based, broader compatibility)
let client = Client::from_dsn("grpc://localhost:50051?tls=false").unwrap();

// With authentication
let client = Client::from_dsn("quic://admin:secret@localhost:3141?insecure_tls_skip_verify=true").unwrap();

// IPv6 addresses use bracket notation
let client = Client::from_dsn("quic://[::1]:3141").unwrap();
```

**Supported Schemes:**

| Scheme | Transport | Description |
|--------|-----------|-------------|
| `quic://` | QUIC | High-performance, low-latency (recommended) |
| `grpc://` | gRPC | HTTP/2 based, broad compatibility |
| `grpcs://` | gRPC + TLS | gRPC with explicit TLS |

**Supported DSN Options:**

- `page_size` - Results page size (default: 1000)
- `hello_name` - Client name (default: "geode-rust-quinn")
- `hello_ver` - Client version (default: "0.1.0")
- `conformance` - GQL conformance level (default: "min")
- `insecure_tls_skip_verify` - Skip TLS verification for QUIC (true/false, default: false)
- `tls` - Enable/disable TLS for gRPC (true/false/1/0, default: true)
- `username` or `user` - Authentication username
- `password` or `pass` - Authentication password

### Configuration Options

- `skip_verify(bool)` - Skip TLS certificate verification (default: false, **insecure**)
- `page_size(usize)` - Results page size (default: 1000)
- `client_name(String)` - Client name sent to server (default: "geode-rust")
- `client_version(String)` - Client version (default: "0.1.0")
- `conformance(String)` - GQL conformance level (default: "min")
- `username(String)` - Authentication username (optional)
- `password(String)` - Authentication password (optional)

## Query Builders

### QueryBuilder

Build queries programmatically:

```rust
let (query, params) = QueryBuilder::new()
    .match_pattern("(p:Person)")
    .where_clause("p.age > 25")
    .return_(&["p.name", "p.age"])
    .order_by(&["p.name"])
    .limit(100)
    .build();
```

### PatternBuilder

Build graph patterns:

```rust
use geode_client::{PatternBuilder, EdgeDirection};

let pattern = PatternBuilder::new()
    .node("a", "Person")
    .edge("knows", "KNOWS", EdgeDirection::Undirected)
    .node("b", "Person")
    .build();
```

## Type System

The client provides a rich type system supporting all GQL data types:

```rust
use geode_client::{Value, ValueKind};

let value = row.get("count").unwrap();

// Type-safe access with Result
let int_val = value.as_int()?;
let str_val = value.as_string()?;
let bool_val = value.as_bool()?;
let decimal_val = value.as_decimal()?;
let array_val = value.as_array()?;
let object_val = value.as_object()?;
let date_val = value.as_date()?;
let timestamp_val = value.as_timestamp()?;

// Check value kind
match value.kind {
    ValueKind::Null => println!("null value"),
    ValueKind::Int => println!("integer: {}", value.as_int()?),
    ValueKind::String => println!("string: {}", value.as_string()?),
    _ => println!("other type"),
}

// Create values programmatically
let int_value = Value::int(42);
let str_value = Value::string("hello");
let bool_value = Value::bool(true);
let array_value = Value::array(vec![Value::int(1), Value::int(2)]);
```

### Supported Types

| Type | Rust Type | Description |
|------|-----------|-------------|
| `Null` | `()` | SQL NULL |
| `Int` | `i64` | 64-bit integer |
| `Bool` | `bool` | Boolean |
| `String` | `String` | UTF-8 string |
| `Decimal` | `rust_decimal::Decimal` | Arbitrary precision decimal |
| `Array` | `Vec<Value>` | Ordered collection |
| `Object` | `HashMap<String, Value>` | Key-value map |
| `Date` | `chrono::NaiveDate` | Calendar date |
| `Timestamp` | `chrono::DateTime<Utc>` | Date and time |
| `Bytea` | `Vec<u8>` | Binary data |

## Error Handling

The client provides comprehensive error types with retry support:

```rust
use geode_client::{Error, Result};

async fn query_with_retry(conn: &mut Connection, query: &str) -> Result<Page> {
    let mut attempts = 0;
    loop {
        match conn.query(query).await {
            Ok((page, _)) => return Ok(page),
            Err(e) if e.is_retryable() && attempts < 3 => {
                attempts += 1;
                tokio::time::sleep(Duration::from_millis(100 * attempts)).await;
                continue;
            }
            Err(e) => return Err(e),
        }
    }
}
```

### Error Types

| Error | Retryable | Description |
|-------|-----------|-------------|
| `Connection` | Yes | Network/QUIC connection issues |
| `Query` | Conditional | Query execution errors (40001, 40P01, 40502 are retryable) |
| `Timeout` | Yes | Operation timed out |
| `Pool` | Yes | Connection pool exhausted |
| `Auth` | No | Authentication failure |
| `Tls` | No | TLS/certificate errors |
| `Validation` | No | Input validation failure |
| `Type` | No | Type conversion errors |

### Validation

The client provides input validation utilities:

```rust
use geode_client::validate;

// Validate queries before sending
validate::query("MATCH (n) RETURN n")?;

// Validate parameter names
validate::param_name("user_id")?;

// Validate connection parameters
validate::hostname("geode.example.com")?;
validate::port(3141)?;
validate::page_size(1000)?;
```

## Examples

The client includes comprehensive examples demonstrating all features:

- `examples/basic.rs` - Simple connection and queries
- `examples/advanced.rs` - Advanced features and patterns
- `examples/transactions.rs` - Transaction management with savepoints

Run examples:

```bash
cargo run --example basic
cargo run --example advanced
cargo run --example transactions
```

## Development

```bash
# Build
cargo build
cargo build --release

# Run tests
cargo test                           # Unit tests (323 tests)
cargo test --test proptest           # Property-based tests (28 tests)
cargo test --features integration    # Integration tests (requires Geode server)

# Run benchmarks
cargo bench                          # All benchmarks (59 benchmarks)
cargo bench -- "query"               # Filter by name

# Run fuzzing (requires nightly)
cargo +nightly fuzz run fuzz_value_from_json

# Code quality
cargo fmt                            # Format code
cargo clippy                         # Lint (0 warnings)
cargo doc --open                     # Generate documentation

# Run examples
cargo run --example basic
cargo run --example advanced
```

## Testing

The library includes comprehensive testing:

| Test Type | Count | Command |
|-----------|-------|---------|
| Unit tests | 323 | `cargo test` |
| Property tests | 28 | `cargo test --test proptest` |
| Integration tests | 36 | `cargo test --features integration` |
| Benchmarks | 59 | `cargo bench` |
| Fuzz targets | 4 | `cargo +nightly fuzz run <target>` |

### Running Integration Tests

Integration tests require a running Geode server:

```bash
# Terminal 1: Start Geode
cd ../geode && ./zig-out/bin/geode serve

# Terminal 2: Run integration tests
cargo test --features integration
```

### Benchmarks

Benchmarks measure performance of key operations:

```bash
# Run all benchmarks
cargo bench

# Sample output:
# value_creation/int      time: [5.2 ns]
# query_builder/complex   time: [198 ns]
# decimal_parsing/4_places time: [8.3 ns]
```

## Performance

The QUIC client provides:

- **Low latency**: ~1-2ms per query (localhost)
- **High throughput**: 10,000+ queries/second with connection pooling
- **Zero-cost abstractions**: Minimal overhead from Rust's type system
- **Concurrent**: Full async support for parallel queries

### System-Level QUIC Optimizations

For optimal QUIC throughput on high-bandwidth connections, configure UDP buffer sizes at the OS level.

**Linux:**

```bash
# Increase UDP buffer sizes to 7MB
sudo sysctl -w net.core.rmem_max=7340032
sudo sysctl -w net.core.wmem_max=7340032

# Persist across reboots
echo "net.core.rmem_max=7340032" | sudo tee -a /etc/sysctl.d/99-geode-quic.conf
echo "net.core.wmem_max=7340032" | sudo tee -a /etc/sysctl.d/99-geode-quic.conf
```

**BSD/macOS:**

```bash
sudo sysctl -w kern.ipc.maxsockbuf=8441037
```

**GSO (Generic Segmentation Offload)**: Automatically enabled on Linux 4.18+ by quinn. Batches UDP packets to reduce syscall overhead.

**Path MTU Discovery (DPLPMTUD)**: Enabled by default in quinn, probes for optimal packet sizes to reduce per-packet overhead.

## Troubleshooting

### Connection Refused

Ensure Geode server is running with QUIC enabled:

```bash
./zig-out/bin/geode serve --listen 0.0.0.0:3141
```

### TLS Verification Errors

For development, you can skip verification:

```rust
let client = Client::new("localhost", 3141).skip_verify(true);
```

For production, ensure proper TLS certificates are configured on the server.

## Contributing

Contributions are welcome! Please follow these guidelines:

1. **Fork and clone** the repository
2. **Create a feature branch** from `main`
3. **Write tests** for new functionality
4. **Run the test suite**: `cargo test`
5. **Run clippy**: `cargo clippy` (must produce 0 warnings)
6. **Format code**: `cargo fmt`
7. **Submit a pull request**

### Code Quality Standards

- All public APIs must have rustdoc comments
- New features require unit tests
- Integration tests for server-facing changes
- Property-based tests for parsers/converters
- Benchmarks for performance-critical code

### Development Setup

```bash
# Clone the repository
git clone https://gitlab.com/devnw/codepros/geode/geode-client-rust.git
cd geode-client-rust

# Install development tools
rustup component add clippy rustfmt

# Run full test suite
cargo test
cargo test --test proptest
cargo clippy
cargo fmt -- --check
```

## License

Apache License 2.0 - see [LICENSE](LICENSE) file for details.

## Related

- [Geode Database]https://gitlab.com/devnw/codepros/geode/geode - Main Geode project
- [Python Client]https://gitlab.com/devnw/codepros/geode/geode-client-python - Official Python client
- [Go Client]https://gitlab.com/devnw/codepros/geode/geode-client-go - Official Go client
- [crates.io]https://crates.io/crates/geode-client - Package on crates.io
- [Documentation]https://gitlab.com/devnw/codepros/geode/geode-client-rust - API documentation