226 lines
5.3 KiB
Markdown
226 lines
5.3 KiB
Markdown
# Kevo Go Client SDK
|
|
|
|
This package provides a Go client for connecting to a Kevo database server. The client uses the gRPC transport layer to communicate with the server and provides an idiomatic Go API for working with Kevo.
|
|
|
|
## Features
|
|
|
|
- Simple key-value operations (Get, Put, Delete)
|
|
- Batch operations for atomic writes
|
|
- Transaction support with ACID guarantees
|
|
- Iterator API for efficient range scans
|
|
- Connection pooling and automatic retries
|
|
- TLS support for secure communication
|
|
- Comprehensive error handling
|
|
- Configurable timeouts and backoff strategies
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
go get github.com/jeremytregunna/kevo
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/jeremytregunna/kevo/pkg/client"
|
|
_ "github.com/jeremytregunna/kevo/pkg/grpc/transport" // Register gRPC transport
|
|
)
|
|
|
|
func main() {
|
|
// Create a client with default options
|
|
options := client.DefaultClientOptions()
|
|
options.Endpoint = "localhost:50051"
|
|
|
|
c, err := client.NewClient(options)
|
|
if err != nil {
|
|
log.Fatalf("Failed to create client: %v", err)
|
|
}
|
|
|
|
// Connect to the server
|
|
ctx := context.Background()
|
|
if err := c.Connect(ctx); err != nil {
|
|
log.Fatalf("Failed to connect: %v", err)
|
|
}
|
|
defer c.Close()
|
|
|
|
// Basic key-value operations
|
|
key := []byte("hello")
|
|
value := []byte("world")
|
|
|
|
// Store a value
|
|
if _, err := c.Put(ctx, key, value, true); err != nil {
|
|
log.Fatalf("Put failed: %v", err)
|
|
}
|
|
|
|
// Retrieve a value
|
|
val, found, err := c.Get(ctx, key)
|
|
if err != nil {
|
|
log.Fatalf("Get failed: %v", err)
|
|
}
|
|
|
|
if found {
|
|
fmt.Printf("Value: %s\n", val)
|
|
} else {
|
|
fmt.Println("Key not found")
|
|
}
|
|
|
|
// Delete a value
|
|
if _, err := c.Delete(ctx, key, true); err != nil {
|
|
log.Fatalf("Delete failed: %v", err)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration Options
|
|
|
|
The client can be configured using the `ClientOptions` struct:
|
|
|
|
```go
|
|
options := client.ClientOptions{
|
|
// Connection options
|
|
Endpoint: "localhost:50051",
|
|
ConnectTimeout: 5 * time.Second,
|
|
RequestTimeout: 10 * time.Second,
|
|
TransportType: "grpc",
|
|
PoolSize: 5,
|
|
|
|
// Security options
|
|
TLSEnabled: true,
|
|
CertFile: "/path/to/cert.pem",
|
|
KeyFile: "/path/to/key.pem",
|
|
CAFile: "/path/to/ca.pem",
|
|
|
|
// Retry options
|
|
MaxRetries: 3,
|
|
InitialBackoff: 100 * time.Millisecond,
|
|
MaxBackoff: 2 * time.Second,
|
|
BackoffFactor: 1.5,
|
|
RetryJitter: 0.2,
|
|
|
|
// Performance options
|
|
Compression: client.CompressionGzip,
|
|
MaxMessageSize: 16 * 1024 * 1024, // 16MB
|
|
}
|
|
```
|
|
|
|
## Transactions
|
|
|
|
```go
|
|
// Begin a transaction
|
|
tx, err := client.BeginTransaction(ctx, false) // readOnly=false
|
|
if err != nil {
|
|
log.Fatalf("Failed to begin transaction: %v", err)
|
|
}
|
|
|
|
// Perform operations within the transaction
|
|
success, err := tx.Put(ctx, []byte("key1"), []byte("value1"))
|
|
if err != nil {
|
|
tx.Rollback(ctx) // Rollback on error
|
|
log.Fatalf("Transaction put failed: %v", err)
|
|
}
|
|
|
|
// Commit the transaction
|
|
if err := tx.Commit(ctx); err != nil {
|
|
log.Fatalf("Transaction commit failed: %v", err)
|
|
}
|
|
```
|
|
|
|
## Scans and Iterators
|
|
|
|
```go
|
|
// Set up scan options
|
|
scanOptions := client.ScanOptions{
|
|
Prefix: []byte("user:"), // Optional prefix
|
|
StartKey: []byte("user:1"), // Optional start key (inclusive)
|
|
EndKey: []byte("user:9"), // Optional end key (exclusive)
|
|
Limit: 100, // Optional limit
|
|
}
|
|
|
|
// Create a scanner
|
|
scanner, err := client.Scan(ctx, scanOptions)
|
|
if err != nil {
|
|
log.Fatalf("Failed to create scanner: %v", err)
|
|
}
|
|
defer scanner.Close()
|
|
|
|
// Iterate through results
|
|
for scanner.Next() {
|
|
fmt.Printf("Key: %s, Value: %s\n", scanner.Key(), scanner.Value())
|
|
}
|
|
|
|
// Check for errors after iteration
|
|
if err := scanner.Error(); err != nil {
|
|
log.Fatalf("Scan error: %v", err)
|
|
}
|
|
```
|
|
|
|
## Batch Operations
|
|
|
|
```go
|
|
// Create a batch of operations
|
|
operations := []client.BatchOperation{
|
|
{Type: "put", Key: []byte("key1"), Value: []byte("value1")},
|
|
{Type: "put", Key: []byte("key2"), Value: []byte("value2")},
|
|
{Type: "delete", Key: []byte("old-key")},
|
|
}
|
|
|
|
// Execute the batch atomically
|
|
success, err := client.BatchWrite(ctx, operations, true)
|
|
if err != nil {
|
|
log.Fatalf("Batch write failed: %v", err)
|
|
}
|
|
```
|
|
|
|
## Error Handling and Retries
|
|
|
|
The client automatically handles retries for transient errors using exponential backoff with jitter. You can configure the retry behavior using the `RetryPolicy` in the client options.
|
|
|
|
```go
|
|
// Manual retry with custom policy
|
|
err = client.RetryWithBackoff(
|
|
ctx,
|
|
func() error {
|
|
_, _, err := c.Get(ctx, key)
|
|
return err
|
|
},
|
|
3, // maxRetries
|
|
100*time.Millisecond, // initialBackoff
|
|
2*time.Second, // maxBackoff
|
|
2.0, // backoffFactor
|
|
0.2, // jitter
|
|
)
|
|
```
|
|
|
|
## Database Statistics
|
|
|
|
```go
|
|
// Get database statistics
|
|
stats, err := client.GetStats(ctx)
|
|
if err != nil {
|
|
log.Fatalf("Failed to get stats: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Key count: %d\n", stats.KeyCount)
|
|
fmt.Printf("Storage size: %d bytes\n", stats.StorageSize)
|
|
fmt.Printf("MemTable count: %d\n", stats.MemtableCount)
|
|
fmt.Printf("SSTable count: %d\n", stats.SstableCount)
|
|
fmt.Printf("Write amplification: %.2f\n", stats.WriteAmplification)
|
|
fmt.Printf("Read amplification: %.2f\n", stats.ReadAmplification)
|
|
```
|
|
|
|
## Compaction
|
|
|
|
```go
|
|
// Trigger compaction
|
|
success, err := client.Compact(ctx, false) // force=false
|
|
if err != nil {
|
|
log.Fatalf("Compaction failed: %v", err)
|
|
}
|
|
``` |