Showing preview only (3,238K chars total). Download the full file or copy to clipboard to get everything.
Repository: benbjohnson/litestream
Branch: main
Commit: ae88b164dd63
Files: 265
Total size: 3.0 MB
Directory structure:
gitextract_ajbk2a5_/
├── .aiexclude
├── .claude/
│ ├── agents/
│ │ ├── ltx-compaction-specialist.md
│ │ ├── performance-optimizer.md
│ │ ├── replica-client-developer.md
│ │ ├── sqlite-expert.md
│ │ └── test-engineer.md
│ ├── commands/
│ │ ├── add-storage-backend.md
│ │ ├── analyze-ltx.md
│ │ ├── debug-ipc.md
│ │ ├── debug-wal.md
│ │ ├── fix-common-issues.md
│ │ ├── run-comprehensive-tests.md
│ │ ├── test-compaction.md
│ │ ├── trace-replication.md
│ │ └── validate-replica.md
│ └── settings.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── commit.yml
│ ├── integration-tests.yml
│ ├── manual-integration-tests.yml
│ ├── pr-metrics.yml
│ ├── pre-release-checklist.yml
│ ├── release.docker.yml
│ ├── release.yml
│ ├── stale-issues.yml
│ └── upgrade-tests.yml
├── .gitignore
├── .goreleaser.yml
├── .markdownlint.json
├── .pre-commit-config.yaml
├── AGENTS.md
├── AI_PR_GUIDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── GEMINI.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── _examples/
│ └── library/
│ ├── README.md
│ ├── basic/
│ │ └── main.go
│ ├── library_example_test.go
│ └── s3/
│ └── main.go
├── abs/
│ └── replica_client.go
├── cmd/
│ ├── litestream/
│ │ ├── databases.go
│ │ ├── directory_watcher.go
│ │ ├── directory_watcher_stress_test.go
│ │ ├── directory_watcher_test.go
│ │ ├── info.go
│ │ ├── info_test.go
│ │ ├── list.go
│ │ ├── list_test.go
│ │ ├── ltx.go
│ │ ├── ltx_test.go
│ │ ├── main.go
│ │ ├── main_notwindows.go
│ │ ├── main_test.go
│ │ ├── main_windows.go
│ │ ├── mcp.go
│ │ ├── register.go
│ │ ├── register_test.go
│ │ ├── replicate.go
│ │ ├── replicate_test.go
│ │ ├── reset.go
│ │ ├── restore.go
│ │ ├── restore_test.go
│ │ ├── start.go
│ │ ├── status.go
│ │ ├── status_test.go
│ │ ├── stop.go
│ │ ├── sync.go
│ │ ├── sync_test.go
│ │ ├── unregister.go
│ │ ├── unregister_test.go
│ │ └── version.go
│ ├── litestream-test/
│ │ ├── README.md
│ │ ├── S3-RETENTION-TESTING.md
│ │ ├── load.go
│ │ ├── main.go
│ │ ├── populate.go
│ │ ├── scripts/
│ │ │ ├── README.md
│ │ │ ├── reproduce-critical-bug.sh
│ │ │ ├── test-754-restore-focus.sh
│ │ │ ├── test-754-s3-scenarios.sh
│ │ │ ├── test-format-isolation.sh
│ │ │ ├── test-massive-upgrade.sh
│ │ │ ├── test-quick-format-check.sh
│ │ │ ├── test-s3-access-point.sh
│ │ │ ├── test-s3-retention-cleanup.sh
│ │ │ ├── test-s3-retention-comprehensive.sh
│ │ │ ├── test-s3-retention-large-db.sh
│ │ │ ├── test-s3-retention-small-db.sh
│ │ │ ├── test-simple-754-reproduction.sh
│ │ │ ├── test-upgrade-large-db.sh
│ │ │ ├── test-upgrade-v0.3-to-v0.5.sh
│ │ │ ├── test-v0.5-flag-reproduction.sh
│ │ │ ├── test-v0.5-restart-scenarios.sh
│ │ │ └── verify-test-setup.sh
│ │ ├── shrink.go
│ │ └── validate.go
│ └── litestream-vfs/
│ ├── chaos_test.go
│ ├── fuzz_test.go
│ ├── hydration_e2e_test.go
│ ├── main.go
│ ├── main_test.go
│ ├── stress_test.go
│ ├── time_travel_test.go
│ ├── vfs_soak_test.go
│ └── vfs_write_integration_test.go
├── compaction_level.go
├── compactor.go
├── compactor_test.go
├── db.go
├── db_internal_test.go
├── db_shutdown_test.go
├── db_test.go
├── docker-compose.test.yml
├── docs/
│ ├── ARCHITECTURE.md
│ ├── DOC_MAINTENANCE.md
│ ├── LTX_FORMAT.md
│ ├── PATTERNS.md
│ ├── PENDING_USER_DOCS.md
│ ├── PROVIDER_COMPATIBILITY.md
│ ├── REPLICA_CLIENT_GUIDE.md
│ ├── SQLITE_INTERNALS.md
│ ├── TESTING_GUIDE.md
│ └── VFS.md
├── etc/
│ ├── build.ps1
│ ├── gon-sign.hcl
│ ├── gon.hcl
│ ├── litestream.service
│ ├── litestream.wxs
│ ├── litestream.yml
│ ├── nfpm.yml
│ ├── run-s3-docker-tests.sh
│ └── s3_mock.py
├── file/
│ ├── replica_client.go
│ └── replica_client_test.go
├── go.mod
├── go.sum
├── grafana/
│ ├── README.md
│ └── litestream-dashboard.json
├── gs/
│ ├── replica_client.go
│ └── replica_client_test.go
├── heartbeat.go
├── heartbeat_test.go
├── internal/
│ ├── hexdump.go
│ ├── internal.go
│ ├── internal_unix.go
│ ├── internal_windows.go
│ ├── limit_read_closer.go
│ ├── lock_unix.go
│ ├── lock_windows.go
│ ├── resumable_reader.go
│ ├── resumable_reader_test.go
│ └── testingutil/
│ └── testingutil.go
├── leaser.go
├── litestream.go
├── litestream_test.go
├── llms.txt
├── log.go
├── mock/
│ └── replica_client.go
├── nats/
│ ├── replica_client.go
│ └── replica_client_test.go
├── oss/
│ ├── replica_client.go
│ └── replica_client_test.go
├── packages/
│ ├── npm/
│ │ ├── litestream-vfs/
│ │ │ ├── README.md
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── litestream-vfs-darwin-amd64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── litestream-vfs-darwin-arm64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── litestream-vfs-linux-amd64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ └── litestream-vfs-linux-arm64/
│ │ ├── README.md
│ │ └── package.json
│ ├── python/
│ │ ├── MANIFEST.in
│ │ ├── README.md
│ │ ├── litestream_vfs/
│ │ │ ├── __init__.py
│ │ │ └── noop.c
│ │ ├── scripts/
│ │ │ └── rename_wheel.py
│ │ └── setup.py
│ └── ruby/
│ ├── README.md
│ ├── lib/
│ │ └── litestream_vfs.rb
│ └── litestream-vfs.gemspec
├── replica.go
├── replica_client.go
├── replica_client_test.go
├── replica_internal_test.go
├── replica_test.go
├── replica_url.go
├── replica_url_test.go
├── restore_fuzz_test.go
├── s3/
│ ├── leaser.go
│ ├── leaser_test.go
│ ├── replica_client.go
│ └── replica_client_test.go
├── scripts/
│ ├── README.md
│ ├── analyze-test-results.sh
│ ├── run-upgrade-tests.sh
│ └── setup-homebrew-tap.sh
├── server.go
├── server_test.go
├── sftp/
│ └── replica_client.go
├── skills/
│ └── litestream/
│ ├── SKILL.md
│ ├── references/
│ │ ├── ARCHITECTURE.md
│ │ ├── LTX_FORMAT.md
│ │ ├── PATTERNS.md
│ │ ├── REPLICA_CLIENT_GUIDE.md
│ │ ├── SQLITE_INTERNALS.md
│ │ └── TESTING_GUIDE.md
│ └── scripts/
│ └── validate-setup.sh
├── src/
│ ├── litestream-vfs.c
│ ├── sqlite3.h
│ ├── sqlite3ext.h
│ └── sqlite3vfs.h
├── store.go
├── store_compaction_remote_test.go
├── store_test.go
├── testdata/
│ ├── db/
│ │ ├── enforce-retention/
│ │ │ └── database
│ │ └── write-snapshot-to/
│ │ └── database
│ └── wal-reader/
│ ├── frame-checksum-mismatch/
│ │ └── wal
│ ├── frame-salts/
│ │ └── wal
│ ├── ok/
│ │ └── wal
│ └── salt-mismatch/
│ └── wal
├── tests/
│ ├── cpu-usage/
│ │ ├── README.md
│ │ ├── litestream-test-polling.yml
│ │ └── test-cpu-usage.sh
│ └── integration/
│ ├── README.md
│ ├── boundary_test.go
│ ├── compatibility_test.go
│ ├── comprehensive_soak_test.go
│ ├── concurrent_test.go
│ ├── directory_watcher_helpers.go
│ ├── directory_watcher_test.go
│ ├── docker_helpers.go
│ ├── fixtures.go
│ ├── helpers.go
│ ├── minio_soak_test.go
│ ├── overnight_s3_soak_test.go
│ ├── overnight_test.go
│ ├── profile_test.go
│ ├── quick_test.go
│ ├── s3_access_point_test.go
│ ├── s3_restore_connection_drop_test.go
│ ├── scenario_test.go
│ ├── shutdown_retry_test.go
│ ├── soak_helpers.go
│ ├── supabase-s3/
│ │ ├── docker-compose.yml
│ │ └── test.sh
│ └── upgrade_test.go
├── v3.go
├── v3_test.go
├── vfs.go
├── vfs_compaction_test.go
├── vfs_test.go
├── vfs_write_test.go
├── wal_reader.go
├── wal_reader_test.go
└── webdav/
├── replica_client.go
└── replica_client_test.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .aiexclude
================================================
# .aiexclude - Files to exclude from Gemini Code Assist
# This file works like .gitignore for AI context
# Sensitive files
*.key
*.pem
*.secret
.env
.env.*
# Build artifacts
bin/
dist/
*.exe
*.dll
*.so
*.dylib
# Test databases
*.db
*.db-wal
*.db-shm
*.sqlite
*.sqlite-wal
*.sqlite-shm
# Large test files
testdata/large/
*.ltx
# Vendor directories
vendor/
# Generated files
*.pb.go
*_generated.go
# Documentation that's redundant with AGENTS.md
docs/RELEASE.md
# CI/CD configs that aren't relevant for code understanding
.github/workflows/release.yml
.goreleaser.yml
# Temporary and backup files
*.tmp
*.bak
*.swp
*~
# OS-specific files
.DS_Store
Thumbs.db
================================================
FILE: .claude/agents/ltx-compaction-specialist.md
================================================
---
role: LTX Format and Compaction Specialist
tools:
- read
- write
- edit
- grep
- bash
priority: high
---
# LTX Compaction Specialist Agent
You are an expert in the LTX (Log Transaction) format and multi-level compaction strategies for Litestream.
## Core Knowledge
### LTX File Format
```
┌─────────────────────┐
│ Header │ 84 bytes
├─────────────────────┤
│ Page Frames │ Variable
├─────────────────────┤
│ Page Index │ Binary search structure
├─────────────────────┤
│ Trailer │ 16 bytes
└─────────────────────┘
```
### File Naming Convention
```
MMMMMMMMMMMMMMMM-NNNNNNNNNNNNNNNN.ltx
Where:
M = MinTXID (16 hex digits)
N = MaxTXID (16 hex digits)
Example: 0000000000000001-0000000000000064.ltx
```
## Default Compaction Levels
### Level Structure
```
Level 0: Raw (no compaction)
Level 1: 30-second windows
Level 2: 5-minute windows
Level 3: 1-hour windows
Snapshots: Daily full database
```
### Critical Compaction Rules
1. **ALWAYS Read Local First**:
```go
// CORRECT - Handles eventual consistency
f, err := os.Open(db.LTXPath(info.Level, info.MinTXID, info.MaxTXID))
if err == nil {
return f, nil // Use local file
}
// Only fall back to remote if local doesn't exist
return replica.Client.OpenLTXFile(...)
```
2. **Preserve Timestamps**:
```go
// Keep earliest CreatedAt
info, err := replica.Client.WriteLTXFile(ctx, level, minTXID, maxTXID, reader)
if err != nil {
return nil, fmt.Errorf("write ltx file: %w", err)
}
info.CreatedAt = oldestSourceFile.CreatedAt
```
3. **Skip Lock Page**:
```go
if pgno == ltx.LockPgno(pageSize) {
continue
}
```
## Compaction Algorithm
```go
func compactLTXFiles(files []*LTXFile) (*LTXFile, error) {
// 1. Create page map (newer overwrites older)
pageMap := make(map[uint32]Page)
for _, file := range files {
for _, page := range file.Pages {
pageMap[page.Number] = page
}
}
// 2. Create new LTX with merged pages
merged := <XFile{
MinTXID: files[0].MinTXID,
MaxTXID: files[len(files)-1].MaxTXID,
}
// 3. Add pages in order (skip lock page!)
for pgno := uint32(1); pgno <= maxPgno; pgno++ {
if pgno == LockPageNumber(pageSize) {
continue
}
if page, ok := pageMap[pgno]; ok {
merged.Pages = append(merged.Pages, page)
}
}
return merged, nil
}
```
## Key Properties
### Immutability
- LTX files are NEVER modified after creation
- New changes create new files
- Compaction creates new merged files
### Checksums
- CRC-64 ECMA for integrity
- `PreApplyChecksum`/`PostApplyChecksum` on the header/trailer bracketing file state
- `FileChecksum` covering the entire file contents
### Page Index
- Exposed via `ltx.DecodePageIndex`
- Tracks page number plus offset/size of the encoded payload
- Located by seeking from the end of the file using trailer metadata
## Common Issues
1. **Partial Reads**: Remote storage may return incomplete files
2. **Race Conditions**: Multiple compactions running
3. **Timestamp Loss**: Not preserving original CreatedAt
4. **Lock Page**: Including 1GB lock page in compacted files
5. **Memory Usage**: Loading entire files for compaction
6. **Corrupted State**: Unclean shutdowns or storage failures can leave corrupted local LTX files, causing "nonsequential page numbers" or "non-contiguous transaction files" errors. Recovery: `litestream reset <db-path>` (manual) or `auto-recover: true` replica config (automatic). See `cmd/litestream/reset.go` and `replica.go`
## Testing
```bash
# Test compaction
go test -v -run TestStore_CompactDB ./...
# Test with eventual consistency
go test -v -run TestStore_CompactDB_RemotePartialRead ./...
# Manual inspection
litestream ltx /path/to/db.sqlite
# For deeper inspection use the Go API (ltx.NewDecoder)
```
## References
- docs/LTX_FORMAT.md - Complete format specification
- store.go - Compaction scheduling
- db.go - Compaction implementation
- github.com/superfly/ltx - LTX library
================================================
FILE: .claude/agents/performance-optimizer.md
================================================
---
role: Performance Optimizer
tools:
- read
- write
- edit
- bash
- grep
priority: medium
---
# Performance Optimizer Agent
You specialize in optimizing Litestream for speed, memory usage, and resource efficiency.
## Key Performance Areas
### O(n) Operations to Optimize
1. **Page Iteration**
```go
// Cache page index
const DefaultEstimatedPageIndexSize = 32 * 1024 // 32KB
// Fetch end of file first for page index
offset := info.Size - DefaultEstimatedPageIndexSize
if offset < 0 {
offset = 0
}
```
2. **File Listing**
```go
// Cache file listings
type FileCache struct {
files []FileInfo
timestamp time.Time
ttl time.Duration
}
```
3. **Compaction**
```go
// Limit concurrent compactions
sem := make(chan struct{}, maxConcurrentCompactions)
```
## Memory Optimization
### Page Buffer Pooling
```go
var pagePool = sync.Pool{
New: func() interface{} {
b := make([]byte, 4096) // Default page size
return &b
},
}
func getPageBuffer() []byte {
return *pagePool.Get().(*[]byte)
}
func putPageBuffer(b []byte) {
pagePool.Put(&b)
}
```
### Streaming Instead of Loading
```go
// BAD - Loads entire file
data, err := os.ReadFile(path)
// GOOD - Streams data
f, err := os.Open(path)
defer f.Close()
io.Copy(dst, f)
```
## Concurrency Patterns
### Proper Locking
```go
// Read-heavy optimization
type Store struct {
mu sync.RWMutex // Use RWMutex for read-heavy
}
func (s *Store) Read() {
s.mu.RLock()
defer s.mu.RUnlock()
// Read operation
}
func (s *Store) Write() {
s.mu.Lock()
defer s.mu.Unlock()
// Write operation
}
```
### Channel Patterns
```go
// Batch processing
batch := make([]Item, 0, batchSize)
ticker := time.NewTicker(batchInterval)
for {
select {
case item := <-input:
batch = append(batch, item)
if len(batch) >= batchSize {
processBatch(batch)
batch = batch[:0]
}
case <-ticker.C:
if len(batch) > 0 {
processBatch(batch)
batch = batch[:0]
}
}
}
```
## I/O Optimization
### Buffered I/O
```go
// Use buffered writers
bw := bufio.NewWriterSize(w, 64*1024) // 64KB buffer
defer bw.Flush()
// Use buffered readers
br := bufio.NewReaderSize(r, 64*1024)
```
### Parallel Downloads
```go
func downloadParallel(files []string) {
var wg sync.WaitGroup
sem := make(chan struct{}, 5) // Limit to 5 concurrent
for _, file := range files {
wg.Add(1)
go func(f string) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
download(f)
}(file)
}
wg.Wait()
}
```
## Caching Strategy
### LRU Cache Implementation
```go
type LRUCache struct {
capacity int
items map[string]*list.Element
list *list.List
mu sync.RWMutex
}
func (c *LRUCache) Get(key string) (interface{}, bool) {
c.mu.RLock()
elem, ok := c.items[key]
c.mu.RUnlock()
if !ok {
return nil, false
}
c.mu.Lock()
c.list.MoveToFront(elem)
c.mu.Unlock()
return elem.Value, true
}
```
## Profiling Tools
### CPU Profiling
```bash
# Generate CPU profile
go test -cpuprofile=cpu.prof -bench=.
# Analyze
go tool pprof cpu.prof
(pprof) top10
(pprof) list functionName
```
### Memory Profiling
```bash
# Generate memory profile
go test -memprofile=mem.prof -bench=.
# Analyze allocations
go tool pprof -alloc_space mem.prof
```
### Trace Analysis
```bash
# Generate trace
go test -trace=trace.out
# View trace
go tool trace trace.out
```
## Configuration Tuning
### SQLite Pragmas
```sql
PRAGMA cache_size = -64000; -- 64MB cache
PRAGMA synchronous = NORMAL; -- Balance safety/speed
PRAGMA wal_autocheckpoint = 10000; -- Larger WAL before checkpoint
PRAGMA busy_timeout = 5000; -- 5 second timeout
```
### Litestream Settings
```yaml
# Optimal intervals
min-checkpoint-page-n: 1000
truncate-page-n: 121359
monitor-interval: 1s
checkpoint-interval: 1m
```
## Benchmarks to Run
```bash
# Core operations
go test -bench=BenchmarkWALRead
go test -bench=BenchmarkLTXWrite
go test -bench=BenchmarkCompaction
go test -bench=BenchmarkPageIteration
# With memory stats
go test -bench=. -benchmem
```
## Common Performance Issues
1. **Not pooling buffers** - Creates garbage
2. **Loading entire files** - Use streaming
3. **Excessive locking** - Use RWMutex
4. **No caching** - Repeated expensive operations
5. **Serial processing** - Could parallelize
6. **Small buffers** - Increase buffer sizes
## References
- Go performance tips: https://go.dev/doc/perf
- SQLite optimization: https://sqlite.org/optoverview.html
- Profiling guide: https://go.dev/blog/pprof
================================================
FILE: .claude/agents/replica-client-developer.md
================================================
---
role: Replica Client Developer
tools:
- read
- write
- edit
- grep
- bash
priority: high
---
# Replica Client Developer Agent
You specialize in implementing and maintaining storage backend clients for Litestream replication.
## Core Knowledge
### ReplicaClient Interface
Every storage backend MUST implement:
```go
type ReplicaClient interface {
Type() string
LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error)
OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error)
WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error)
DeleteLTXFiles(ctx context.Context, files []*ltx.FileInfo) error
DeleteAll(ctx context.Context) error
}
```
**LTXFiles useMetadata parameter**:
- When `useMetadata=true`: Fetch accurate timestamps from backend metadata (slower, required for point-in-time restore)
- When `useMetadata=false`: Use fast timestamps from file listing (faster, suitable for replication monitoring)
### Critical Patterns
1. **Eventual Consistency Handling**:
- Storage may not immediately reflect writes
- Files may be partially available
- ALWAYS prefer local files during compaction
2. **Atomic Operations**:
```go
// Write to temp, then rename
tmpPath := path + ".tmp"
// Write to tmpPath
os.Rename(tmpPath, path)
```
3. **Error Types**:
- Return `os.ErrNotExist` for missing files
- Wrap errors with context: `fmt.Errorf("operation: %w", err)`
4. **ResumableReader Support**:
- `OpenLTXFile` MUST support the `offset` parameter for range requests
- `internal/resumable_reader.go` wraps streams with auto-reconnection on idle timeouts
- During restore, streams may sit idle while compactor processes other files
- If `offset` is ignored, restore operations fail on connection timeouts
### ReplicaClientV3 Interface (Optional)
Backends supporting v0.3.x backward-compatible restore should implement:
```go
type ReplicaClientV3 interface {
GenerationsV3(ctx context.Context) ([]string, error)
SnapshotsV3(ctx context.Context, generation string) ([]SnapshotInfoV3, error)
WALSegmentsV3(ctx context.Context, generation string) ([]WALSegmentInfoV3, error)
OpenSnapshotV3(ctx context.Context, generation string, index int) (io.ReadCloser, error)
OpenWALSegmentV3(ctx context.Context, generation string, index int, offset int64) (io.ReadCloser, error)
}
```
See `v3.go` for type definitions and `s3/replica_client.go` for reference implementation.
## Implementation Checklist
### New Backend Requirements
- [ ] Implement ReplicaClient interface
- [ ] Handle partial reads (offset/size)
- [ ] Support seek parameter for pagination
- [ ] Preserve CreatedAt timestamps when metadata is available
- [ ] Handle eventual consistency
- [ ] Implement proper error types
- [ ] Add integration tests
- [ ] Document configuration
### Testing Requirements
```bash
# Integration test
go test -v ./replica_client_test.go -integration [backend]
# Race conditions
go test -race -v ./[backend]/...
# Large files (>1GB)
./bin/litestream-test populate -target-size 2GB
```
## Existing Backends Reference
### Study These Implementations
- `s3/replica_client.go` - AWS S3 (most complete)
- `gs/replica_client.go` - Google Cloud Storage
- `abs/replica_client.go` - Azure Blob Storage
- `file/replica_client.go` - Local filesystem (simplest)
- `sftp/replica_client.go` - SSH File Transfer
- `nats/replica_client.go` - NATS JetStream (newest)
- `oss/replica_client.go` - Alibaba Cloud OSS
## Common Pitfalls
1. Not handling eventual consistency
2. Missing atomic write operations
3. Incorrect error types
4. Not preserving timestamps
5. Forgetting partial read support
6. No retry logic for transient failures
7. Not supporting `offset` parameter in `OpenLTXFile` — breaks `ResumableReader` during restore
## Configuration Pattern
```yaml
replica:
type: [backend]
option1: value1
option2: value2
```
## References
- docs/REPLICA_CLIENT_GUIDE.md - Complete implementation guide
- replica_client.go - Interface definition
- v3.go - ReplicaClientV3 interface and v0.3.x types
- internal/resumable_reader.go - ResumableReader for restore resilience
- replica_client_test.go - Test suite
================================================
FILE: .claude/agents/sqlite-expert.md
================================================
---
role: SQLite WAL and Page Expert
tools:
- read
- write
- edit
- grep
- bash
priority: high
---
# SQLite Expert Agent
You are a SQLite internals expert specializing in WAL (Write-Ahead Log) operations and page management for the Litestream project.
## Core Knowledge
### Critical SQLite Concepts
1. **1GB Lock Page** (MUST KNOW):
- Located at exactly 0x40000000 (1,073,741,824 bytes)
- Page number varies by page size:
- 4KB pages: 262145
- 8KB pages: 131073
- 16KB pages: 65537
- 32KB pages: 32769
- MUST be skipped in all iterations
- Cannot contain data
2. **WAL Structure**:
- 32-byte header with magic number
- Frames with 24-byte headers
- Cumulative checksums
- Salt values for verification
3. **Page Types**:
- B-tree interior/leaf pages
- Overflow pages
- Freelist pages
- Lock byte page (at 1GB)
## Primary Responsibilities
### WAL Monitoring
- Monitor WAL file changes in `db.go`
- Ensure proper checksum verification
- Handle WAL frame reading correctly
- Convert WAL frames to LTX format
### Page Management
- Always skip lock page during iteration
- Handle various page sizes correctly
- Verify page integrity
- Manage page caching efficiently
### Testing Requirements
- Create test databases >1GB
- Test all page sizes (4KB, 8KB, 16KB, 32KB)
- Verify lock page skipping
- Test WAL checkpoint modes
## Code Patterns
### Correct Lock Page Handling
```go
lockPgno := ltx.LockPgno(pageSize)
if pgno == lockPgno {
continue // Skip lock page
}
```
### WAL Reading
```go
// Always verify magic number
magic := binary.BigEndian.Uint32(header.Magic[:])
if magic != 0x377f0682 && magic != 0x377f0683 {
return errors.New("invalid WAL magic")
}
```
## Common Mistakes to Avoid
1. Not skipping lock page at 1GB
2. Incorrect checksum calculations
3. Wrong byte order (use BigEndian)
4. Not handling all page sizes
5. Direct file manipulation (use SQLite API)
## References
- docs/SQLITE_INTERNALS.md - Complete SQLite internals guide
- docs/LTX_FORMAT.md - LTX conversion details
- db.go - WAL monitoring implementation
================================================
FILE: .claude/agents/test-engineer.md
================================================
---
role: Test Engineer
tools:
- read
- write
- edit
- bash
- grep
priority: medium
---
# Test Engineer Agent
You specialize in creating and maintaining comprehensive test suites for Litestream, with focus on edge cases and race conditions.
## Critical Test Scenarios
### 1GB Lock Page Testing
**MUST TEST**: Databases crossing the 1GB boundary
```bash
# Create >1GB test database
sqlite3 large.db <<EOF
PRAGMA page_size=4096;
CREATE TABLE test(data BLOB);
WITH RECURSIVE generate_series(value) AS (
SELECT 1 UNION ALL SELECT value+1 FROM generate_series LIMIT 300000
)
INSERT INTO test SELECT randomblob(4000) FROM generate_series;
EOF
# Verify lock page handling
./bin/litestream replicate large.db file:///tmp/replica
./bin/litestream restore -o restored.db file:///tmp/replica
sqlite3 restored.db "PRAGMA integrity_check;"
```
### Race Condition Testing
**ALWAYS** use race detector:
```bash
go test -race -v ./...
# Specific areas prone to races
go test -race -v -run TestReplica_Sync ./...
go test -race -v -run TestDB_Sync ./...
go test -race -v -run TestStore_CompactDB ./...
```
## Test Categories
### Unit Tests
```go
func TestLockPageCalculation(t *testing.T) {
testCases := []struct {
pageSize int
expected uint32
}{
{4096, 262145},
{8192, 131073},
{16384, 65537},
{32768, 32769},
}
for _, tc := range testCases {
got := ltx.LockPgno(tc.pageSize)
if got != tc.expected {
t.Errorf("pageSize=%d: got %d, want %d",
tc.pageSize, got, tc.expected)
}
}
}
```
### Integration Tests
```bash
# Backend-specific tests
go test -v ./replica_client_test.go -integration s3
go test -v ./replica_client_test.go -integration gcs
go test -v ./replica_client_test.go -integration abs
go test -v ./replica_client_test.go -integration sftp
```
### Eventual Consistency Tests
```go
func TestEventualConsistency(t *testing.T) {
// Simulate delayed file appearance
// Simulate partial file reads
// Verify local file preference
}
```
## Test Data Generation
### Various Page Sizes
```bash
for size in 4096 8192 16384 32768; do
./bin/litestream-test populate \
-db test-${size}.db \
-page-size ${size} \
-target-size 2GB
done
```
### Compaction Scenarios
```bash
# Exercise store-level compaction logic
go test -v -run TestStore_CompactDB ./...
# Include remote partial-read coverage
go test -v -run TestStore_CompactDB_RemotePartialRead ./...
```
## Performance Testing
### Benchmark Template
```go
func BenchmarkCompaction(b *testing.B) {
// Setup test files
files := generateTestLTXFiles(100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
compactLTXFiles(files)
}
}
```
### Memory Profiling
```bash
go test -bench=. -benchmem -memprofile mem.prof
go tool pprof mem.prof
```
## Error Injection
### Simulate Failures
```go
type FailingReplicaClient struct {
litestream.ReplicaClient
failAfter int
count int
}
func (c *FailingReplicaClient) WriteLTXFile(...) error {
c.count++
if c.count > c.failAfter {
return errors.New("simulated failure")
}
return c.ReplicaClient.WriteLTXFile(...)
}
```
## Coverage Requirements
### Minimum Coverage
- Core packages: >80%
- Storage backends: >70%
- Critical paths: 100%
### Generate Coverage Report
```bash
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```
## Common Test Mistakes
1. Not testing with databases >1GB
2. Forgetting race detector
3. Not testing all page sizes
4. Missing eventual consistency tests
5. No error injection tests
6. Ignoring benchmark regressions
## CI/CD Integration
```yaml
# .github/workflows/test.yml
- name: Run tests with race detector
run: go test -race -v ./...
- name: Test large databases
run: ./scripts/test-large-db.sh
- name: Integration tests
run: ./scripts/test-integration.sh
```
## References
- docs/TESTING_GUIDE.md - Complete testing guide
- replica_client_test.go - Integration test patterns
- db_test.go - Unit test examples
================================================
FILE: .claude/commands/add-storage-backend.md
================================================
---
description: Create a new storage backend implementation
---
# Add Storage Backend Command
Create a new storage backend implementation for Litestream with all required components.
## Steps
1. **Create Package Directory**
```bash
mkdir -p {{backend_name}}
```
2. **Implement ReplicaClient Interface**
Create `{{backend_name}}/replica_client.go`:
```go
package {{backend_name}}
type ReplicaClient struct {
// Configuration fields
}
func (c *ReplicaClient) Type() string {
return "{{backend_name}}"
}
func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ltx.TXID, useMetadata bool) (ltx.FileIterator, error) {
// List files at level
// When useMetadata=true, fetch accurate timestamps from backend metadata
}
func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (io.ReadCloser, error) {
// Open file for reading
}
func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {
// Write file atomically
}
func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, files []*ltx.FileInfo) error {
// Delete files
}
func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
// Remove all files for replica
}
```
3. **Add Configuration Parsing**
Update `cmd/litestream/config.go`:
```go
case "{{backend_name}}":
client = &{{backend_name}}.ReplicaClient{
// Parse config
}
```
4. **Create Integration Tests**
Create `{{backend_name}}/replica_client_test.go`:
```go
func TestReplicaClient_{{backend_name}}(t *testing.T) {
if !*integration || *backend != "{{backend_name}}" {
t.Skip("{{backend_name}} integration test skipped")
}
// Test implementation
}
```
5. **Add Documentation**
Update README.md with configuration example:
```yaml
replica:
type: {{backend_name}}
option1: value1
option2: value2
```
## Key Requirements
- Handle eventual consistency
- Implement atomic writes (temp file + rename)
- Support partial reads (offset/size)
- Preserve CreatedAt timestamps in returned FileInfo
- Return proper error types (os.ErrNotExist)
## Testing
```bash
# Run integration tests
go test -v ./replica_client_test.go -integration {{backend_name}}
# Test with race detector
go test -race -v ./{{backend_name}}/...
```
================================================
FILE: .claude/commands/analyze-ltx.md
================================================
Analyze LTX file issues in Litestream. This command helps diagnose problems with LTX files, including corruption, missing files, and consistency issues.
First, understand the context:
- What error messages are being reported?
- Which storage backend is being used?
- Are there any eventual consistency issues?
Then perform the analysis:
1. **Check LTX file structure**: Look for corrupted headers, invalid page indices, or checksum mismatches in the LTX files.
2. **Verify file continuity**: Ensure there are no gaps in the TXID sequence that could prevent restoration.
3. **Check compaction issues**: Look for problems during compaction that might corrupt files, especially with eventually consistent storage.
4. **Analyze page sequences**: Verify that page numbers are sequential and the lock page at 1GB is properly skipped.
5. **Review storage backend behavior**: Check if the storage backend has eventual consistency that might cause partial reads during compaction.
Key files to examine:
- `db.go`: WAL monitoring and LTX generation
- `replica_client.go`: Storage interface
- `store.go`: Compaction logic
- Backend-specific client in `s3/`, `gs/`, etc.
Common issues to look for:
- "nonsequential page numbers" errors (corrupted compaction)
- "EOF" errors (partial file reads)
- Missing TXID ranges (failed uploads)
- Lock page at 0x40000000 not being skipped
Use the testing harness to reproduce:
```bash
./bin/litestream-test validate -source-db test.db -replica-url [URL]
```
## Recovery Options
When analysis reveals corrupted or missing LTX files, two recovery mechanisms are available:
**Manual recovery** with `litestream reset`:
```bash
litestream reset /path/to/database.db
```
Clears local LTX state from the metadata directory. The database file is not modified.
Next sync creates a fresh snapshot. See `cmd/litestream/reset.go` for implementation.
**Automatic recovery** with `auto-recover` config:
```yaml
dbs:
- path: /path/to/database.db
replicas:
- url: s3://bucket/path
auto-recover: true
```
When enabled, Litestream automatically resets local state when LTX errors are detected
during sync. Disabled by default. See `replica.go` (auto-recover logic) for implementation.
================================================
FILE: .claude/commands/debug-ipc.md
================================================
---
description: Debug IPC Unix socket issues
---
# Debug IPC Command
Diagnose issues with the Litestream IPC control socket (`server.go`).
## 1. Socket Configuration Check
Verify the socket is enabled and correctly configured:
```yaml
# litestream.yml
socket:
enabled: true # Default: false
path: /var/run/litestream.sock # Default path
permissions: 0600 # Default permissions
```
Source: `SocketConfig` struct in `server.go:17-21`, defaults in `DefaultSocketConfig()`.
## 2. Endpoint Reference
All endpoints are HTTP over Unix socket (`server.go:74-87`):
| Method | Path | Request Body | Response |
|--------|------|-------------|----------|
| `GET` | `/info` | — | `{version, pid, uptime_seconds, started_at, database_count}` |
| `GET` | `/list` | — | `{databases: [{path, status, last_sync_at}]}` |
| `GET` | `/txid?path=` | — | `{txid}` |
| `POST` | `/register` | `{path, replica_url}` | `{status, path}` |
| `POST` | `/unregister` | `{path, timeout?}` | `{status, path}` |
| `POST` | `/start` | `{path, timeout?}` | `{status, path}` |
| `POST` | `/stop` | `{path, timeout?}` | `{status, path}` |
| `GET` | `/debug/pprof/` | — | Standard Go pprof index |
| `GET` | `/debug/pprof/profile` | — | CPU profile |
| `GET` | `/debug/pprof/trace` | — | Execution trace |
## 3. Testing with curl
```bash
# Server info (version, PID, uptime)
curl --unix-socket /var/run/litestream.sock http://localhost/info
# List all managed databases
curl --unix-socket /var/run/litestream.sock http://localhost/list
# Get transaction ID for a specific database
curl --unix-socket /var/run/litestream.sock "http://localhost/txid?path=/path/to/db"
# Register a new database at runtime
curl --unix-socket /var/run/litestream.sock -X POST \
-H "Content-Type: application/json" \
-d '{"path":"/path/to/db","replica_url":"s3://bucket/path"}' \
http://localhost/register
# Unregister (stop replicating) a database
curl --unix-socket /var/run/litestream.sock -X POST \
-H "Content-Type: application/json" \
-d '{"path":"/path/to/db"}' \
http://localhost/unregister
# CPU profile (30 seconds by default)
curl --unix-socket /var/run/litestream.sock http://localhost/debug/pprof/profile > cpu.prof
go tool pprof cpu.prof
# Heap profile
curl --unix-socket /var/run/litestream.sock http://localhost/debug/pprof/heap > heap.prof
go tool pprof heap.prof
```
## 4. Common Issues
### Socket not enabled
**Symptom**: `curl: (7) Couldn't connect to server`
**Fix**: Set `socket.enabled: true` in config and restart litestream.
### Permission denied
**Symptom**: `curl: (7) Permission denied`
**Fix**: Check `socket.permissions` (default `0600`). The connecting user must match the litestream process owner.
### Stale socket file after crash
**Symptom**: `bind: address already in use` in logs on startup
**Fix**: Remove the stale socket file: `rm /var/run/litestream.sock`
### Database not found
**Symptom**: `{"error":"database not found"}` from `/txid` or `/start`
**Fix**: Verify the path matches the expanded path in config. Use `/list` to see registered databases. Note that `$PID` and env vars are expanded in config paths.
### Register fails with "already exists"
**Symptom**: `{"error":"database already registered"}`
**Fix**: The database path is already being replicated. Use `/unregister` first, then `/register` with new settings.
## 5. pprof Debugging
Available pprof endpoints on the IPC socket:
```bash
# Interactive CPU profile analysis
curl -s --unix-socket /var/run/litestream.sock \
http://localhost/debug/pprof/profile?seconds=30 > cpu.prof
go tool pprof -http=:8080 cpu.prof
# Goroutine dump (useful for deadlock investigation)
curl --unix-socket /var/run/litestream.sock \
http://localhost/debug/pprof/goroutine?debug=2
# Memory allocation profile
curl -s --unix-socket /var/run/litestream.sock \
http://localhost/debug/pprof/heap > heap.prof
go tool pprof -http=:8080 heap.prof
# Execution trace (captures scheduler, GC, goroutine events)
curl -s --unix-socket /var/run/litestream.sock \
http://localhost/debug/pprof/trace?seconds=5 > trace.out
go tool trace trace.out
```
================================================
FILE: .claude/commands/debug-wal.md
================================================
Debug WAL monitoring issues in Litestream. This command helps diagnose problems with WAL change detection, checkpointing, and replication triggers.
First, understand the symptoms:
- Is replication not triggering on changes?
- Are checkpoints failing or not happening?
- Is the WAL growing unbounded?
Then debug the monitoring system:
1. **Check monitor goroutine** (db.go:1499):
```go
// Verify monitor is running
func (db *DB) monitor() {
ticker := time.NewTicker(db.MonitorInterval) // Default: 1s
// Check if ticker is firing
// Verify checkWAL() is being called
}
```
2. **Verify WAL change detection**:
```go
// Check if WAL changes are detected
func (db *DB) checkWAL() (bool, error) {
// Get WAL size and checksum
// Compare with previous values
// Should return true if changed
}
```
3. **Debug checkpoint triggers**:
```go
// Check checkpoint thresholds
MinCheckpointPageN int // Default: 1000 pages
TruncatePageN int // Default: 121359 pages
// Verify WAL page count
walPageCount := db.WALPageCount()
if walPageCount > db.MinCheckpointPageN {
// Should trigger passive checkpoint
}
if walPageCount > db.TruncatePageN {
// Should trigger truncate checkpoint (emergency brake)
}
```
4. **Check long-running read transaction**:
```go
// Ensure rtx is maintained
if db.rtx == nil {
// Read transaction lost - replication may fail
}
```
5. **Monitor notification channel**:
```go
// Check if replicas are notified
select {
case <-db.notify:
// WAL change detected
default:
// No changes
}
```
Common issues to check:
- MonitorInterval too long (default 1s)
- Checkpoint failing due to active transactions
- Read transaction preventing checkpoint
- Notify channel not triggering replicas
- WAL file permissions issues
Debug commands:
```sql
-- Check WAL status
PRAGMA wal_checkpoint;
PRAGMA journal_mode;
PRAGMA page_count;
PRAGMA wal_autocheckpoint;
-- Check for locks
SELECT * FROM pragma_lock_status();
```
Testing WAL monitoring:
```go
func TestDB_WALMonitoring(t *testing.T) {
db := setupTestDB(t)
// Set fast monitoring for test
db.MonitorInterval = 10 * time.Millisecond
// Write data
writeTestData(t, db, 100)
// Wait for notification
select {
case <-db.notify:
// Success
case <-time.After(1 * time.Second):
t.Error("WAL change not detected")
}
}
```
Monitor with logging:
```go
slog.Debug("wal check",
"size", walInfo.Size,
"checksum", walInfo.Checksum,
"pages", walInfo.PageCount)
```
================================================
FILE: .claude/commands/fix-common-issues.md
================================================
---
description: Fix common Litestream issues
---
# Fix Common Issues Command
Diagnose and fix common issues in Litestream deployments.
## Issue 1: Lock Page Not Being Skipped
**Symptom**: Errors or corruption with databases >1GB
**Check**:
```bash
# Find lock page references
grep -r "LockPgno" --include="*.go"
```
**Fix**:
```go
// Ensure all page iterations skip lock page
lockPgno := ltx.LockPgno(pageSize)
if pgno == lockPgno {
continue
}
```
## Issue 2: Race Condition in Replica Position
**Symptom**: Data races detected, inconsistent position tracking
**Check**:
```bash
go test -race -v -run TestReplica_Sync ./...
```
**Fix**:
```go
// Change from RLock to Lock for writes
func (r *Replica) SetPos(pos ltx.Pos) {
r.mu.Lock() // NOT RLock!
defer r.mu.Unlock()
r.pos = pos
}
```
## Issue 3: Eventual Consistency Issues
**Symptom**: Compaction failures, partial file reads
**Check**:
```bash
# Look for remote reads during compaction
grep -r "OpenLTXFile" db.go | grep -v "os.Open"
```
**Fix**:
```go
// Always try local first
f, err := os.Open(db.LTXPath(info.Level, info.MinTXID, info.MaxTXID))
if err == nil {
return f, nil
}
// Only fall back to remote if local doesn't exist
return replica.Client.OpenLTXFile(...)
```
## Issue 4: CreatedAt Timestamp Loss
**Symptom**: Point-in-time recovery lacks accurate timestamps
**Check**:
```go
info, err := client.WriteLTXFile(ctx, level, minTXID, maxTXID, r)
if err != nil {
t.Fatal(err)
}
if info.CreatedAt.IsZero() {
t.Fatal("CreatedAt not set")
}
```
**Fix**:
```go
// Ensure storage metadata is copied into the returned FileInfo
modTime := resp.LastModified
info.CreatedAt = modTime
```
## Issue 5: Non-Atomic File Writes
**Symptom**: Partial files, corruption on crash
**Check**:
```bash
# Find direct writes without temp files
grep -r "os.Create\|os.WriteFile" --include="*.go"
```
**Fix**:
```go
// Write to temp, then rename
tmpPath := path + ".tmp"
if err := os.WriteFile(tmpPath, data, 0644); err != nil {
return err
}
return os.Rename(tmpPath, path)
```
## Issue 6: WAL Checkpoint Blocking
**Symptom**: WAL grows indefinitely, database locks
**Check**:
```sql
-- Check WAL size
PRAGMA wal_checkpoint(PASSIVE);
SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();
```
**Fix**:
```go
// Release read transaction periodically
db.rtx.Rollback()
db.rtx = nil
// Checkpoint
db.db.Exec("PRAGMA wal_checkpoint(RESTART)")
// Restart read transaction
db.initReadTx()
```
## Issue 7: Memory Leaks
**Symptom**: Growing memory usage over time
**Check**:
```bash
# Generate heap profile
go test -memprofile=mem.prof -run=XXX -bench=.
go tool pprof -top mem.prof
```
**Fix**:
```go
// Use sync.Pool for buffers
var pagePool = sync.Pool{
New: func() interface{} {
b := make([]byte, pageSize)
return &b
},
}
// Close resources properly
defer func() {
if f != nil {
f.Close()
}
}()
```
## Issue 8: Corrupted or Missing LTX Files
**Symptom**: Sync failures with errors like "ltx validation failed", "nonsequential page numbers",
"non-contiguous transaction files", or persistent sync retry backoff loops after unclean shutdowns.
**Check**:
```bash
# Look for LTXError messages in logs
rg -i "ltx.*error|reset.*local|auto.recover" /var/log/litestream.log
# Check if meta directory has corrupted state (note dot prefix)
ls -la /path/to/.database.db-litestream/ltx/
```
**Fix - Manual Reset**:
```bash
# Clears local LTX state, forces fresh snapshot on next sync
# Database file is NOT modified
litestream reset /path/to/database.db
# With explicit config file
litestream reset -config /etc/litestream.yml /path/to/database.db
```
**Fix - Automatic Recovery**:
```yaml
# Add to replica config in litestream.yml
dbs:
- path: /path/to/database.db
replicas:
- url: s3://bucket/path
auto-recover: true # Automatically resets on LTX errors
```
**When to use which**: Use `auto-recover` for unattended deployments where automatic recovery
is preferred over manual intervention. Use manual `reset` when you want to investigate the
corruption first. `auto-recover` is disabled by default because resetting discards local LTX
history, which may reduce point-in-time restore granularity.
**Reference**: `cmd/litestream/reset.go`, `replica.go` (auto-recover logic), `db.go` (`ResetLocalState`)
## Issue 9: IPC Socket Connection Failures
**Symptom**: `connection refused` or `no such file or directory` when using the control socket
**Check**:
```bash
# Verify socket exists and has correct permissions
ls -la /var/run/litestream.sock
# Verify socket is enabled in config
rg -A3 'socket:' /etc/litestream.yml
# Check if litestream process is running
pgrep -a litestream
```
**Fix**:
```yaml
# Enable socket in litestream.yml
socket:
enabled: true
path: /var/run/litestream.sock
permissions: 0600
```
**Stale socket**: If litestream crashed, the socket file may still exist. The process creates a new socket on startup and will fail if the stale file exists. Remove it manually:
```bash
rm /var/run/litestream.sock
```
**Reference**: `server.go` (`SocketConfig`, `Server.Start`)
## Issue 10: Backup Files Accumulating (Retention Disabled)
**Symptom**: Storage usage growing unbounded, old LTX files never deleted
**Check**:
```bash
# Look for retention warning in logs
rg "retention disabled" /var/log/litestream.log
```
**Fix**:
```yaml
# Option 1: Re-enable Litestream retention (default)
retention:
enabled: true
# Option 2: Keep disabled, but configure cloud lifecycle policies
# Example: S3 lifecycle rule to expire objects in ltx/ prefix after 30 days
```
**Reference**: `store.go` (`SetRetentionEnabled`), `compactor.go` (`RetentionEnabled`), `cmd/litestream/replicate.go:295`
## Issue 11: v0.3.x Restore Not Finding Backups
**Symptom**: Restore fails with "no snapshots available" but v0.3.x backups exist in storage
**Check**:
```bash
# Verify v0.3.x backup structure exists
aws s3 ls s3://bucket/path/generations/ --recursive | head -20
```
**Fix**: Ensure the backend implements `ReplicaClientV3`. The S3 backend supports this automatically. The restore process checks for v0.3.x backups when no v0.4.x+ backup is found.
**Reference**: `v3.go` (`ReplicaClientV3` interface), `s3/replica_client.go`
## Diagnostic Commands
```bash
# Check database integrity
sqlite3 database.db "PRAGMA integrity_check;"
# List replicated LTX files
litestream ltx /path/to/db.sqlite
# Check replication status
litestream databases
# Reset corrupted local state
litestream reset /path/to/database.db
# Test restoration
litestream restore -o test.db [replica-url]
# IPC socket diagnostics (requires socket.enabled: true)
curl --unix-socket /var/run/litestream.sock http://localhost/info
curl --unix-socket /var/run/litestream.sock http://localhost/list
curl --unix-socket /var/run/litestream.sock "http://localhost/txid?path=/path/to/db"
```
## Prevention Checklist
- [ ] Always test with databases >1GB
- [ ] Run with race detector in CI
- [ ] Test all page sizes (4KB, 8KB, 16KB, 32KB)
- [ ] Verify eventual consistency handling
- [ ] Check for proper locking (Lock vs RLock)
- [ ] Ensure atomic file operations
- [ ] Preserve timestamps in compaction
================================================
FILE: .claude/commands/run-comprehensive-tests.md
================================================
---
description: Run comprehensive test suite for Litestream
---
# Run Comprehensive Tests Command
Execute a full test suite including unit tests, integration tests, race detection, and large database tests.
## Quick Test Suite
```bash
# Basic tests with race detection
go test -race -v ./...
# With coverage
go test -race -cover -v ./...
```
## Full Test Suite
### 1. Unit Tests
```bash
echo "=== Running Unit Tests ==="
go test -v ./... -short
```
### 2. Race Condition Tests
```bash
echo "=== Testing for Race Conditions ==="
go test -race -v -run TestReplica_Sync ./...
go test -race -v -run TestDB_Sync ./...
go test -race -v -run TestStore_CompactDB ./...
go test -race -v ./...
```
### 3. Integration Tests
```bash
echo "=== Running Integration Tests ==="
# S3 (requires AWS credentials)
AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=yyy \
go test -v ./replica_client_test.go -integration s3
# Google Cloud Storage (requires credentials)
GOOGLE_APPLICATION_CREDENTIALS=/path/to/creds.json \
go test -v ./replica_client_test.go -integration gcs
# Azure Blob Storage
AZURE_STORAGE_ACCOUNT=xxx AZURE_STORAGE_KEY=yyy \
go test -v ./replica_client_test.go -integration abs
# SFTP (requires SSH server)
go test -v ./replica_client_test.go -integration sftp
# File system (always available)
go test -v ./replica_client_test.go -integration file
```
### 4. Large Database Tests (>1GB)
```bash
echo "=== Testing Large Databases ==="
# Create test database for each page size
for pagesize in 4096 8192 16384 32768; do
echo "Testing page size: $pagesize"
# Create >1GB database
sqlite3 test-${pagesize}.db <<EOF
PRAGMA page_size=${pagesize};
CREATE TABLE test(id INTEGER PRIMARY KEY, data BLOB);
WITH RECURSIVE generate_series(value) AS (
SELECT 1 UNION ALL SELECT value+1 FROM generate_series LIMIT 300000
)
INSERT INTO test SELECT value, randomblob(4000) FROM generate_series;
EOF
# Test replication
./bin/litestream replicate test-${pagesize}.db file:///tmp/replica-${pagesize} &
PID=$!
sleep 10
kill $PID
# Test restoration
./bin/litestream restore -o restored-${pagesize}.db file:///tmp/replica-${pagesize}
# Verify integrity
sqlite3 restored-${pagesize}.db "PRAGMA integrity_check;" | grep -q "ok" || echo "FAILED: Page size $pagesize"
# Cleanup
rm -f test-${pagesize}.db restored-${pagesize}.db
rm -rf /tmp/replica-${pagesize}
done
```
### 5. Compaction Tests
```bash
echo "=== Testing Compaction ==="
# Test store compaction
go test -v -run TestStore_CompactDB ./...
# Test with eventual consistency simulation
go test -v -run TestStore_CompactDB_RemotePartialRead ./...
# Test parallel compaction
go test -race -v -run TestStore_ParallelCompact ./...
```
### 6. Benchmark Tests
```bash
echo "=== Running Benchmarks ==="
# Core operations
go test -bench=BenchmarkWALRead -benchmem
go test -bench=BenchmarkLTXWrite -benchmem
go test -bench=BenchmarkCompaction -benchmem
go test -bench=BenchmarkPageIteration -benchmem
# Compare with previous results
go test -bench=. -benchmem | tee bench_new.txt
# benchcmp bench_old.txt bench_new.txt # if you have previous results
```
### 7. Memory and CPU Profiling
```bash
echo "=== Profiling ==="
# Memory profile
go test -memprofile=mem.prof -bench=. -run=^$
go tool pprof -top mem.prof
# CPU profile
go test -cpuprofile=cpu.prof -bench=. -run=^$
go tool pprof -top cpu.prof
```
### 8. Build Tests
```bash
echo "=== Testing Builds ==="
# Test main build (no CGO)
go build -o bin/litestream ./cmd/litestream
# Test VFS build (requires CGO)
make vfs
# Test cross-compilation
GOOS=linux GOARCH=amd64 go build -o bin/litestream-linux-amd64 ./cmd/litestream
GOOS=darwin GOARCH=arm64 go build -o bin/litestream-darwin-arm64 ./cmd/litestream
GOOS=windows GOARCH=amd64 go build -o bin/litestream-windows-amd64.exe ./cmd/litestream
```
### 9. Linting and Static Analysis
```bash
echo "=== Running Linters ==="
# Format check
gofmt -d .
goimports -local github.com/benbjohnson/litestream -d .
# Vet
go vet ./...
# Static check
staticcheck ./...
# Pre-commit hooks
pre-commit run --all-files
```
## Coverage Report
```bash
# Generate coverage for all packages
go test -coverprofile=coverage.out ./...
# View coverage in terminal
go tool cover -func=coverage.out
# Generate HTML report
go tool cover -html=coverage.out -o coverage.html
open coverage.html # macOS
# xdg-open coverage.html # Linux
```
## CI-Friendly Test Script
```bash
#!/bin/bash
set -e
echo "Running Litestream Test Suite"
# Unit tests with race and coverage
go test -race -cover -v ./... | tee test_results.txt
# Check coverage threshold
coverage=$(go test -cover ./... | grep -oE '[0-9]+\.[0-9]+%' | head -1 | tr -d '%')
if (( $(echo "$coverage < 70" | bc -l) )); then
echo "Coverage $coverage% is below 70% threshold"
exit 1
fi
# Large database test
./scripts/test-large-db.sh
echo "All tests passed!"
```
## Expected Results
✅ All tests should pass
✅ No race conditions detected
✅ Coverage >70% for core packages
✅ Lock page correctly skipped for all page sizes
✅ Restoration works for databases >1GB
✅ No memory leaks in benchmarks
================================================
FILE: .claude/commands/test-compaction.md
================================================
Test Litestream compaction logic. This command helps test and debug compaction issues, especially with eventually consistent storage backends.
First, understand the test scenario:
- What storage backend needs testing?
- What size database is involved?
- Are there eventual consistency concerns?
Then create comprehensive tests:
1. **Test basic compaction**:
```go
func TestCompaction_Basic(t *testing.T) {
// Create multiple LTX files at level 0
// Run compaction to level 1
// Verify merged file is correct
}
```
2. **Test with eventual consistency**:
```go
func TestStore_CompactDB_RemotePartialRead(t *testing.T) {
// Use mock client that returns partial data initially
// Verify compaction prefers local files
// Ensure no corruption occurs
}
```
3. **Test lock page handling during compaction**:
```go
func TestCompaction_LockPage(t *testing.T) {
// Create database > 1GB
// Compact with data around lock page
// Verify lock page is skipped (page at 0x40000000)
}
```
4. **Test timestamp preservation**:
```go
func TestCompaction_PreserveTimestamps(t *testing.T) {
// Compact files with different CreatedAt times
// Verify earliest timestamp is preserved
}
```
Key areas to test:
- Reading from local files first (db.go:1280-1294)
- Skipping lock page at 1GB boundary
- Preserving CreatedAt timestamps
- Handling partial/incomplete remote files
- Concurrent compaction safety
Run with race detector:
```bash
go test -race -v -run TestStore_CompactDB ./...
```
Use the test harness for large databases:
```bash
./bin/litestream-test populate -db test.db -target-size 1.5GB
./bin/litestream-test validate -source-db test.db -replica-url file:///tmp/replica
```
================================================
FILE: .claude/commands/trace-replication.md
================================================
Trace the complete replication flow in Litestream. This command helps understand how changes flow from SQLite through to storage backends.
Follow the replication path step by step:
1. **Application writes to SQLite**:
```sql
-- Application performs write
INSERT INTO table VALUES (...);
-- SQLite appends to WAL file
```
2. **DB.monitor() syncs the shadow WAL** (db.go:1499):
```go
ticker := time.NewTicker(db.MonitorInterval) // default 1s
for {
select {
case <-db.ctx.Done():
return
case <-ticker.C:
}
if err := db.Sync(db.ctx); err != nil && !errors.Is(err, context.Canceled) {
db.Logger.Error("sync error", "error", err)
}
}
```
3. **Replica.monitor() responds** (replica.go):
```go
ticker := time.NewTicker(r.SyncInterval)
defer ticker.Stop()
notify := r.db.Notify()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Enforce minimum sync interval
case <-notify:
// WAL changed, time to sync
}
notify = r.db.Notify()
if err := r.Sync(ctx); err != nil && !errors.Is(err, context.Canceled) {
r.Logger().Error("monitor error", "error", err)
}
}
```
4. **Replica.Sync() uploads new L0 files** (replica.go):
```go
// Determine local database position
dpos, err := r.db.Pos()
if err != nil {
return err
}
if dpos.IsZero() {
return fmt.Errorf("no position, waiting for data")
}
// Upload each unreplicated L0 file
for txID := r.Pos().TXID + 1; txID <= dpos.TXID; txID = r.Pos().TXID + 1 {
if err := r.uploadLTXFile(ctx, 0, txID, txID); err != nil {
return err
}
r.SetPos(ltx.Pos{TXID: txID})
}
```
5. **ReplicaClient uploads to storage**:
```go
func (c *S3Client) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {
// Stream LTX data to storage and return metadata (size, CreatedAt, checksums)
}
```
6. **Checkpoint when thresholds are hit**:
```go
if walPageCount > db.MinCheckpointPageN {
db.Checkpoint(ctx, litestream.CheckpointModePassive)
}
if walPageCount > db.TruncatePageN {
db.Checkpoint(ctx, litestream.CheckpointModeTruncate)
}
```
Key synchronization points:
- WAL monitoring (1s intervals)
- Replica sync (configurable, default 1s)
- Checkpoint triggers (page thresholds)
- Compaction (hourly/daily)
Trace with logging:
```go
// Enable debug logging
slog.SetLogLevel(slog.LevelDebug)
// Key log points:
slog.Debug("wal changed", "size", walSize)
slog.Debug("syncing replica", "pos", r.Pos())
slog.Debug("ltx uploaded", "txid", maxTXID)
slog.Debug("checkpoint complete", "mode", mode)
```
Performance metrics to monitor:
- WAL growth rate
- Sync latency
- Upload throughput
- Checkpoint frequency
- Compaction duration
Common bottlenecks:
1. Slow storage uploads
2. Large transactions causing big LTX files
3. Long-running read transactions blocking checkpoints
4. Eventual consistency delays
5. Network latency to storage
Test replication flow:
```bash
# Start replication with verbose logging
litestream replicate -v
# In another terminal, write to database
sqlite3 test.db "INSERT INTO test VALUES (1, 'data');"
# Watch logs for flow:
# - WAL change detected
# - Replica sync triggered
# - LTX file uploaded
# - Position updated
```
Verify replication:
```bash
# List replicated files
aws s3 ls s3://bucket/path/ltx/0000/
# Restore and verify
litestream restore -o restored.db s3://bucket/path
sqlite3 restored.db "SELECT * FROM test;"
```
================================================
FILE: .claude/commands/validate-replica.md
================================================
Validate a ReplicaClient implementation in Litestream. This command helps ensure a replica client correctly implements the interface and handles edge cases.
First, identify what needs validation:
- Which replica client implementation?
- What storage backend specifics?
- Any known issues or concerns?
Then validate the implementation:
1. **Interface compliance check**:
```go
// Ensure all methods are implemented
var _ litestream.ReplicaClient = (*YourClient)(nil)
```
1. **Verify error types**:
```go
// OpenLTXFile must return os.ErrNotExist for missing files
_, err := client.OpenLTXFile(ctx, 0, 999, 999, 0, 0)
if !errors.Is(err, os.ErrNotExist) {
t.Errorf("Expected os.ErrNotExist, got %v", err)
}
```
1. **Test partial reads**:
```go
// Must support offset and size parameters
rc, err := client.OpenLTXFile(ctx, 0, 1, 100, 50, 25)
data, _ := io.ReadAll(rc)
if len(data) != 25 {
t.Errorf("Expected 25 bytes, got %d", len(data))
}
```
1. **Verify timestamp preservation**:
```go
// CreatedAt should reflect remote object metadata (or upload time)
start := time.Now()
info, _ := client.WriteLTXFile(ctx, 0, 1, 100, reader)
if info.CreatedAt.IsZero() || info.CreatedAt.Before(start.Add(-time.Second)) {
t.Error("unexpected CreatedAt timestamp")
}
```
1. **Test eventual consistency handling**:
- Implement retry logic for transient failures
- Handle partial file availability
- Verify write-after-write consistency
1. **Validate cleanup**:
```go
// DeleteAll must remove everything
err := client.DeleteAll(ctx)
files, _ := client.LTXFiles(ctx, 0, 0, false)
if files.Next() {
t.Error("Files remain after DeleteAll")
}
```
Key validation points:
- Proper error types (os.ErrNotExist, os.ErrPermission)
- Context cancellation handling
- Concurrent operation safety
- Iterator doesn't load all files at once
- Proper path construction for storage backend
Run integration tests:
```bash
go test -v ./[backend]/replica_client_test.go -integration
```
================================================
FILE: .claude/settings.json
================================================
{
"project_name": "Litestream",
"description": "Standalone disaster recovery tool for SQLite",
"auto_formatting": {
"enabled": true,
"markdown": {
"enabled": true,
"tool": "markdownlint"
},
"json": {
"enabled": true,
"tool": "jq"
}
},
"file_permissions": {
"read_only_patterns": [
"*.pdf"
]
}
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve Litestream
title: ''
labels: bug
assignees: ''
---
<!--- Please provide as much detail as possible to help us reproduce and fix the issue -->
## Bug Description
A clear and concise description of the bug.
## Environment
**Litestream version:**
<!--- Run `litestream version` and paste output here -->
```text
paste output here
```
**Operating system & version:**
<!--- e.g., Ubuntu 22.04, macOS 14.0, Windows 11 -->
**Installation method:**
<!--- e.g., Docker, binary download, built from source -->
**Storage backend:**
<!--- e.g., S3, GCS, Azure, SFTP, file -->
## Steps to Reproduce
<!--- Provide a minimal reproducible example if possible -->
1. Step one
2. Step two
3. Step three
**Expected behavior:**
<!--- What you expected to happen -->
**Actual behavior:**
<!--- What actually happened -->
## Configuration
<!--- Include relevant parts of your litestream.yml -->
<!--- IMPORTANT: Remove sensitive information like credentials, keys, and URLs -->
<details><summary>litestream.yml</summary>
```yaml
# Paste your configuration here
```
</details>
## Logs
<!--- Include relevant error messages or logs -->
<!--- For more detail, run litestream with -trace flag -->
<!--- For long logs, consider using https://gist.github.com -->
<details><summary>Log output</summary>
```text
# Paste relevant logs here
```
</details>
## Additional Context
Add any other context about the problem here (e.g., recent changes, related issues, workarounds you've tried).
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Documentation Issues
url: https://github.com/benbjohnson/litestream.io/issues/new/choose
about: Report documentation bugs or suggest improvements
- name: Documentation
url: https://litestream.io
about: Check our documentation for setup and usage guides
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for Litestream
title: ''
labels: enhancement
assignees: ''
---
<!--- Please search existing issues to avoid duplicates -->
<!--- Use 👍 reactions to upvote existing feature requests -->
## Feature Description
<!--- Provide a clear description of the feature you'd like to see -->
## Use Cases
<!--- Describe the specific problem you're trying to solve -->
<!--- What is your end goal? This helps us understand the context -->
## Attempted Solutions
<!--- What workarounds or alternatives have you tried? -->
<!--- How are you currently handling this use case? -->
## Proposal
<!--- Optional: If you have ideas for implementation, share them here -->
<!--- This could include configuration examples or API designs -->
================================================
FILE: .github/pull_request_template.md
================================================
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- Link to issue if applicable: -->
Fixes #(issue number)
## How Has This Been Tested?
<!--- Describe how you tested your changes -->
<!--- Include details of your testing environment if relevant -->
## Types of changes
<!--- What types of changes does your code introduce? -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (would cause existing functionality to not work as expected)
## Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply -->
- [ ] My code follows the code style of this project (`go fmt`, `go vet`)
- [ ] I have tested my changes (`go test ./...`)
- [ ] I have updated the documentation accordingly (if needed)
================================================
FILE: .github/workflows/commit.yml
================================================
on:
push:
branches:
- main
pull_request:
types:
- opened
- synchronize
- reopened
name: Commit
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: |
go install golang.org/x/tools/cmd/goimports@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
export PATH="$HOME/go/bin:$PATH"
- uses: pre-commit/action@v3.0.0
vfs-build-test:
name: VFS Build Test (macOS)
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build VFS shared library
run: make vfs
- name: Verify shared library created
run: file dist/litestream-vfs.so
vfs-build-test-linux:
name: VFS Build Test (Linux)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build VFS Linux AMD64
run: make vfs-linux-amd64
- name: Verify shared library created
run: file dist/litestream-vfs-linux-amd64.so
build-windows:
name: Build Windows
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install -y mingw-w64
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: |
go build ./cmd/litestream/
file ./litestream.exe
env:
CGO_ENABLED: "1"
GOOS: windows
GOARCH: amd64
CC: x86_64-w64-mingw32-gcc
docker-smoke-test:
name: Docker Smoke Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build default image
uses: docker/build-push-action@v6
with:
context: .
target: default
push: false
load: true
tags: litestream:default
- name: Build hardened image
uses: docker/build-push-action@v6
with:
context: .
target: hardened
push: false
load: true
tags: litestream:hardened
- name: "Default: verify version command"
run: docker run --rm litestream:default version
- name: "Hardened: verify version command"
run: docker run --rm litestream:hardened version
- name: "Hardened: verify non-root user (UID 65532)"
run: |
user=$(docker inspect --format='{{.Config.User}}' litestream:hardened)
echo "Container user: $user"
if [ "$user" != "nonroot:nonroot" ]; then
echo "FAIL: Expected user 'nonroot:nonroot', got '$user'"
exit 1
fi
docker create --name uid-check litestream:hardened version
uid=$(docker export uid-check | tar -xf - --to-stdout etc/passwd | grep '^nonroot:' | cut -d: -f3)
docker rm uid-check
echo "nonroot UID: $uid"
if [ "$uid" != "65532" ]; then
echo "FAIL: Expected UID 65532, got '$uid'"
exit 1
fi
- name: "Hardened: verify no shell exists"
run: |
if docker run --rm --entrypoint /bin/sh litestream:hardened -c "echo hello" 2>/dev/null; then
echo "FAIL: Shell should not exist in scratch image"
exit 1
fi
echo "PASS: No shell in scratch image"
- name: "Hardened: verify CA certificates present"
run: |
docker create --name cert-check litestream:hardened version
docker export cert-check | tar -tf - | grep -q "etc/ssl/certs/ca-certificates.crt"
echo "PASS: CA certificates found"
docker rm cert-check
build:
name: Build & Unit Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go build -o /dev/null ./_examples/library/basic
- run: go build -o /dev/null ./_examples/library/s3
- run: go test -v ./_examples/library
- run: go test -v .
- run: go test -v ./internal
- run: go test -v ./abs
- run: go test -v ./file
- run: go test -v ./gs
- run: go test -v ./nats
- run: go test -v ./s3
- run: go test -v ./sftp
- run: go test -v ./cmd/litestream
# long-running-test:
# name: Run Long Running Unit Test
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - uses: actions/setup-go@v2
# with:
# go-version: '1.24'
# - uses: actions/cache@v2
# with:
# path: ~/go/pkg/mod
# key: ${{ inputs.os }}-go-${{ hashFiles('**/go.sum') }}
# restore-keys: ${{ inputs.os }}-go-
#
# - run: go install ./cmd/litestream
# - run: go test -v -run=TestCmd_Replicate_LongRunning ./integration -long-running-duration 1m
s3-mock-test:
name: Run S3 Mock Tests
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
# cache: 'pip'
- run: pip install moto[s3,server]
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: ./etc/s3_mock.py go test -v ./replica_client_test.go -integration s3
minio-integration-test:
name: Run MinIO Integration Tests
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Start MinIO Server
run: |
docker run -d \
--name minio-test \
-p 9000:9000 \
-p 9001:9001 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
quay.io/minio/minio server /data --console-address ":9001"
# Wait for MinIO to be ready
echo "Waiting for MinIO server to be ready..."
for i in {1..30}; do
if docker exec minio-test mc alias set local http://localhost:9000 minioadmin minioadmin 2>/dev/null; then
echo "MinIO server is ready"
break
fi
echo "Waiting for MinIO server... ($i/30)"
sleep 1
done
# Create test bucket
docker exec minio-test mc mb local/testbucket
- run: go install ./cmd/litestream
- name: Test Query Parameter Support
run: |
# Create test database
sqlite3 /tmp/test.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT); INSERT INTO users (name) VALUES ('Alice'), ('Bob');"
# Test replicate with query parameters
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
# Run replication for 5 seconds
timeout 5 litestream replicate /tmp/test.db \
"s3://testbucket/test.db?endpoint=localhost:9000&forcePathStyle=true" || true
# Verify files were uploaded
docker exec minio-test mc ls local/testbucket/test.db/
# Test restore with query parameters
rm -f /tmp/restored.db
litestream restore -o /tmp/restored.db \
"s3://testbucket/test.db?endpoint=localhost:9000&forcePathStyle=true"
# Verify restored data
if [ "$(sqlite3 /tmp/restored.db 'SELECT COUNT(*) FROM users;')" != "2" ]; then
echo "ERROR: Restored database does not have expected data"
exit 1
fi
echo "MinIO integration test with query parameters passed!"
- name: Cleanup
if: always()
run: |
docker stop minio-test || true
docker rm minio-test || true
nats-docker-test:
name: Run NATS Integration Tests
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Start NATS Server with JetStream
run: |
docker run -d \
--name nats-test \
-p 4222:4222 \
-p 8222:8222 \
nats:latest \
-js \
-DV
# Wait for NATS to be ready
echo "Waiting for NATS server to be ready..."
for i in {1..30}; do
if nc -z localhost 4222; then
echo "NATS server is ready"
break
fi
echo "Waiting for NATS server... ($i/30)"
sleep 1
done
- name: Create NATS Object Store Bucket
run: |
# Install NATS CLI - get latest version using jq for JSON parsing
NATS_VERSION=$(curl -fsSL -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/repos/nats-io/natscli/releases/latest \
| jq -r '.tag_name' \
| sed 's/^v//')
if [ -z "$NATS_VERSION" ] || [ "$NATS_VERSION" = "null" ]; then
NATS_VERSION="0.3.1"
echo "Warning: Failed to fetch latest NATS CLI version, using fallback v${NATS_VERSION}"
fi
wget "https://github.com/nats-io/natscli/releases/download/v${NATS_VERSION}/nats-${NATS_VERSION}-linux-amd64.zip" -O nats.zip
unzip nats.zip
sudo mv "nats-${NATS_VERSION}-linux-amd64/nats" /usr/local/bin/
sudo chmod +x /usr/local/bin/nats
# Create the object store bucket
nats object add litestream-test --max-bucket-size=100M --replicas=1
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration nats
env:
LITESTREAM_NATS_URL: "nats://localhost:4222"
LITESTREAM_NATS_BUCKET: "litestream-test"
- name: Cleanup
if: always()
run: |
docker stop nats-test || true
docker rm nats-test || true
s3-integration-test:
name: Run S3 Integration Tests
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
concurrency:
group: integration-test-s3
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration s3
env:
LITESTREAM_S3_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}
LITESTREAM_S3_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_S3_SECRET_ACCESS_KEY }}
LITESTREAM_S3_REGION: us-east-1
LITESTREAM_S3_BUCKET: integration.litestream.io
gcp-integration-test:
name: Run GCP Integration Tests
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
concurrency:
group: integration-test-gcp
steps:
- name: Extract GCP credentials
run: 'echo "$GOOGLE_APPLICATION_CREDENTIALS" > /opt/gcp.json'
shell: bash
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{secrets.GOOGLE_APPLICATION_CREDENTIALS}}
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration gs
env:
GOOGLE_APPLICATION_CREDENTIALS: /opt/gcp.json
LITESTREAM_GS_BUCKET: integration.litestream.io
abs-integration-test:
name: Run Azure Blob Store Integration Tests
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
concurrency:
group: integration-test-abs
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration abs
env:
LITESTREAM_ABS_ACCOUNT_NAME: ${{ secrets.LITESTREAM_ABS_ACCOUNT_NAME }}
LITESTREAM_ABS_ACCOUNT_KEY: ${{ secrets.LITESTREAM_ABS_ACCOUNT_KEY }}
LITESTREAM_ABS_BUCKET: integration
r2-integration-test:
name: Run Cloudflare R2 Integration Tests
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
concurrency:
group: integration-test-r2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration -replica-clients=r2
env:
LITESTREAM_R2_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_R2_ACCESS_KEY_ID }}
LITESTREAM_R2_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_R2_SECRET_ACCESS_KEY }}
LITESTREAM_R2_ENDPOINT: ${{ secrets.LITESTREAM_R2_ENDPOINT }}
LITESTREAM_R2_BUCKET: ${{ secrets.LITESTREAM_R2_BUCKET }}
sftp-integration-test:
name: Run SFTP Integration Tests
runs-on: ubuntu-latest
needs: build
steps:
- name: Prepare OpenSSH server
run: |-
sudo mkdir -p /test/etc/ssh /test/home /run/sshd /test/data/
sudo ssh-keygen -t ed25519 -f /test/etc/ssh/id_ed25519_host -N ""
sudo ssh-keygen -t ed25519 -f /test/etc/ssh/id_ed25519 -N ""
sudo chmod 0600 /test/etc/ssh/id_ed25519_host /test/etc/ssh/id_ed25519
sudo chmod 0644 /test/etc/ssh/id_ed25519_host.pub /test/etc/ssh/id_ed25519.pub
sudo cp /test/etc/ssh/id_ed25519 /test/id_ed25519
sudo chown $USER /test/id_ed25519
sudo tee /test/etc/ssh/sshd_config <<EOF
Port 2222
HostKey /test/etc/ssh/id_ed25519_host
AuthorizedKeysFile /test/etc/ssh/id_ed25519.pub
AuthenticationMethods publickey
Subsystem sftp internal-sftp
UsePAM no
LogLevel DEBUG
EOF
sudo /usr/sbin/sshd -e -f /test/etc/ssh/sshd_config -E /test/debug.log
- name: Test OpenSSH server works with pubkey auth
run: ssh -v -i /test/id_ed25519 -o StrictHostKeyChecking=accept-new -p 2222 root@localhost whoami || (sudo cat /test/debug.log && exit 1)
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration sftp
env:
LITESTREAM_SFTP_HOST: "localhost:2222"
LITESTREAM_SFTP_USER: "root"
LITESTREAM_SFTP_KEY_PATH: /test/id_ed25519
LITESTREAM_SFTP_PATH: /test/data
- name: Test SFTP with concurrent writes enabled (default)
run: |
cat > /tmp/sftp-concurrent.yml <<EOF
dbs:
- path: /tmp/test-concurrent.db
replica:
type: sftp
host: localhost:2222
key-path: /test/id_ed25519
user: root
path: /test/data/concurrent
concurrent-writes: true
EOF
# Create test database
sqlite3 /tmp/test-concurrent.db <<SQL
CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT);
INSERT INTO test (data) VALUES ('test1'), ('test2'), ('test3');
SQL
# Run replication briefly
timeout 5 ./bin/litestream replicate -config /tmp/sftp-concurrent.yml || true
# Check files were created
ssh -i /test/id_ed25519 -o StrictHostKeyChecking=accept-new -p 2222 root@localhost "ls -la /test/data/concurrent/" || true
- name: Test SFTP with concurrent writes disabled
run: |
cat > /tmp/sftp-sequential.yml <<EOF
dbs:
- path: /tmp/test-sequential.db
replica:
type: sftp
host: localhost:2222
key-path: /test/id_ed25519
user: root
path: /test/data/sequential
concurrent-writes: false
EOF
# Create test database
sqlite3 /tmp/test-sequential.db <<SQL
CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT);
INSERT INTO test (data) VALUES ('test1'), ('test2'), ('test3');
SQL
# Run replication briefly
timeout 5 ./bin/litestream replicate -config /tmp/sftp-sequential.yml || true
# Check files were created
ssh -i /test/id_ed25519 -o StrictHostKeyChecking=accept-new -p 2222 root@localhost "ls -la /test/data/sequential/" || true
webdav-integration-test:
name: Run WebDAV Integration Tests
runs-on: ubuntu-latest
needs: build
steps:
- name: Start WebDAV Server
run: |
docker run -d \
--name webdav-test \
-p 8080:80 \
-e USERNAME=testuser \
-e PASSWORD=testpass \
-v /tmp/webdav:/var/webdav \
bytemark/webdav
# Wait for WebDAV to be ready
for i in {1..30}; do
if curl -s -u testuser:testpass http://localhost:8080/ >/dev/null 2>&1; then
echo "WebDAV server is ready"
break
fi
sleep 1
done
# Verify WebDAV is accessible
curl -u testuser:testpass http://localhost:8080/ || (docker logs webdav-test && exit 1)
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- run: go env
- run: go install ./cmd/litestream
- run: go test -v ./replica_client_test.go -integration webdav
env:
LITESTREAM_WEBDAV_URL: "http://localhost:8080"
LITESTREAM_WEBDAV_USERNAME: "testuser"
LITESTREAM_WEBDAV_PASSWORD: "testpass"
LITESTREAM_WEBDAV_PATH: "/testdata"
- name: Cleanup
if: always()
run: |
docker stop webdav-test || true
docker rm webdav-test || true
================================================
FILE: .github/workflows/integration-tests.yml
================================================
name: Integration Tests
on:
pull_request:
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- 'tests/integration/**'
- '.github/workflows/integration-tests.yml'
workflow_dispatch:
inputs:
test_type:
description: 'Test type to run'
required: false
default: 'quick'
type: choice
options:
- 'quick'
- 'all'
- 'long'
permissions:
contents: read
jobs:
quick-tests:
name: Quick Integration Tests
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || inputs.test_type == 'quick' || inputs.test_type == 'all'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build binaries
run: |
go build -o bin/litestream ./cmd/litestream
go build -o bin/litestream-test ./cmd/litestream-test
- name: Run quick integration tests
run: |
go test -v -tags=integration -timeout=30m ./tests/integration/... \
-run="TestFreshStart|TestDatabaseIntegrity|TestRapidCheckpoints|TestS3AccessPointLocalStack|TestRestore_|TestBinaryCompatibility|TestCompaction_Compatibility|TestVersionMigration|TestUpgrade|TestLockPage|TestDirectoryWatcher|TestDatabaseDeletion|TestWALGrowth|TestBusyTimeout|TestConcurrentOperations"
env:
CGO_ENABLED: 1
- name: Upload test logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: quick-test-logs
path: |
/tmp/litestream-*/*.log
/tmp/*-test.log
scenario-tests:
name: Scenario Integration Tests
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && (inputs.test_type == 'all' || inputs.test_type == 'long')
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build binaries
run: |
go build -o bin/litestream ./cmd/litestream
go build -o bin/litestream-test ./cmd/litestream-test
- name: Run all scenario tests
run: |
go test -v -tags=integration -timeout=1h ./tests/integration/... \
-run="Test(FreshStart|DatabaseIntegrity|DatabaseDeletion|RapidCheckpoints|WALGrowth|ConcurrentOperations|BusyTimeout)"
env:
CGO_ENABLED: 1
- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: scenario-test-logs
path: |
/tmp/litestream-*/*.log
/tmp/*-test.log
long-running-tests:
name: Long-Running Integration Tests
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && inputs.test_type == 'long'
timeout-minutes: 600
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build binaries
run: |
go build -o bin/litestream ./cmd/litestream
go build -o bin/litestream-test ./cmd/litestream-test
- name: Run long tests
run: |
go test -v -tags="integration,long" -timeout=10h ./tests/integration/... \
-run="TestOvernight|Test1GBBoundary"
env:
CGO_ENABLED: 1
- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: long-test-logs
path: |
/tmp/litestream-*/*.log
/tmp/*-test.log
summary:
name: Test Summary
runs-on: ubuntu-latest
needs: [quick-tests]
if: always() && (github.event_name == 'pull_request' || inputs.test_type == 'quick' || inputs.test_type == 'all')
steps:
- name: Generate summary
run: |
echo "## Integration Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.quick-tests.result }}" == "success" ]; then
echo "✅ **Quick Tests:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.quick-tests.result }}" == "failure" ]; then
echo "❌ **Quick Tests:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.quick-tests.result }}" == "skipped" ]; then
echo "⏭️ **Quick Tests:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "**Triggered by:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
# Note: Scenario and long-running tests run independently on workflow_dispatch.
# Check individual job results for those test suites.
================================================
FILE: .github/workflows/manual-integration-tests.yml
================================================
name: Manual Integration Tests
permissions:
issues: write
pull-requests: write
on:
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request number to test (optional)'
required: false
type: string
branch:
description: 'Branch name to test (optional, ignored if PR number is provided)'
required: false
type: string
default: ''
test_s3:
description: 'Run S3 integration tests (AWS)'
required: false
default: true
type: boolean
test_gcs:
description: 'Run Google Cloud Storage integration tests'
required: false
default: false
type: boolean
test_abs:
description: 'Run Azure Blob Storage integration tests'
required: false
default: false
type: boolean
test_tigris:
description: 'Run Fly.io Tigris integration tests'
required: false
default: false
type: boolean
test_r2:
description: 'Run Cloudflare R2 integration tests'
required: false
default: false
type: boolean
test_b2:
description: 'Run Backblaze B2 integration tests'
required: false
default: false
type: boolean
test_multipart:
description: 'Run multipart upload stress tests (5MB-50MB files)'
required: false
default: false
type: boolean
jobs:
setup:
name: Setup and Validation
runs-on: ubuntu-latest
outputs:
ref: ${{ steps.get-ref.outputs.ref }}
ref_name: ${{ steps.get-ref.outputs.ref_name }}
steps:
- name: Determine checkout ref
id: get-ref
run: |
if [ -n "${{ github.event.inputs.pr_number }}" ]; then
echo "ref=refs/pull/${{ github.event.inputs.pr_number }}/head" >> $GITHUB_OUTPUT
echo "ref_name=PR #${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
echo "::notice::Testing PR #${{ github.event.inputs.pr_number }}"
elif [ -n "${{ github.event.inputs.branch }}" ]; then
echo "ref=refs/heads/${{ github.event.inputs.branch }}" >> $GITHUB_OUTPUT
echo "ref_name=branch ${{ github.event.inputs.branch }}" >> $GITHUB_OUTPUT
echo "::notice::Testing branch ${{ github.event.inputs.branch }}"
else
echo "ref=${{ github.ref }}" >> $GITHUB_OUTPUT
echo "ref_name=branch ${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "::notice::Testing current branch ${{ github.ref_name }}"
fi
- name: Validate inputs
run: |
if [ "${{ github.event.inputs.test_s3 }}" != "true" ] && \
[ "${{ github.event.inputs.test_gcs }}" != "true" ] && \
[ "${{ github.event.inputs.test_abs }}" != "true" ] && \
[ "${{ github.event.inputs.test_tigris }}" != "true" ] && \
[ "${{ github.event.inputs.test_r2 }}" != "true" ] && \
[ "${{ github.event.inputs.test_b2 }}" != "true" ] && \
[ "${{ github.event.inputs.test_multipart }}" != "true" ]; then
echo "::error::At least one test type must be selected"
exit 1
fi
echo "### Test Configuration" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Target:** ${{ steps.get-ref.outputs.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Integration Tests:**" >> $GITHUB_STEP_SUMMARY
echo "- S3 (AWS): ${{ github.event.inputs.test_s3 }}" >> $GITHUB_STEP_SUMMARY
echo "- Google Cloud Storage: ${{ github.event.inputs.test_gcs }}" >> $GITHUB_STEP_SUMMARY
echo "- Azure Blob Storage: ${{ github.event.inputs.test_abs }}" >> $GITHUB_STEP_SUMMARY
echo "- Fly.io Tigris: ${{ github.event.inputs.test_tigris }}" >> $GITHUB_STEP_SUMMARY
echo "- Cloudflare R2: ${{ github.event.inputs.test_r2 }}" >> $GITHUB_STEP_SUMMARY
echo "- Backblaze B2: ${{ github.event.inputs.test_b2 }}" >> $GITHUB_STEP_SUMMARY
echo "- Multipart Stress Tests: ${{ github.event.inputs.test_multipart }}" >> $GITHUB_STEP_SUMMARY
s3-integration:
name: S3 Integration Tests (AWS)
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_s3 == 'true'
concurrency:
group: integration-test-s3-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::S3 integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run S3 integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=s3
env:
LITESTREAM_S3_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}
LITESTREAM_S3_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_S3_SECRET_ACCESS_KEY }}
LITESTREAM_S3_REGION: us-east-1
LITESTREAM_S3_BUCKET: integration.litestream.io
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/s3-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: s3-test-results
path: |
*.log
test-results/
tigris-integration:
name: Tigris Integration Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_tigris == 'true'
concurrency:
group: integration-test-tigris-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_TIGRIS_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::Tigris integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run Tigris integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=tigris
env:
LITESTREAM_TIGRIS_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_TIGRIS_ACCESS_KEY_ID }}
LITESTREAM_TIGRIS_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_TIGRIS_SECRET_ACCESS_KEY }}
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/tigris-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: tigris-test-results
path: |
*.log
test-results/
r2-integration:
name: Cloudflare R2 Integration Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_r2 == 'true'
concurrency:
group: integration-test-r2-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_R2_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::R2 integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run R2 integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=r2
env:
LITESTREAM_R2_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_R2_ACCESS_KEY_ID }}
LITESTREAM_R2_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_R2_SECRET_ACCESS_KEY }}
LITESTREAM_R2_ENDPOINT: ${{ secrets.LITESTREAM_R2_ENDPOINT }}
LITESTREAM_R2_BUCKET: ${{ secrets.LITESTREAM_R2_BUCKET }}
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/r2-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: r2-test-results
path: |
*.log
test-results/
b2-integration:
name: Backblaze B2 Integration Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_b2 == 'true'
concurrency:
group: integration-test-b2-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_B2_KEY_ID }}" ]; then
echo "::notice title=Skipped::B2 integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run B2 integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=b2
env:
LITESTREAM_B2_KEY_ID: ${{ secrets.LITESTREAM_B2_KEY_ID }}
LITESTREAM_B2_APPLICATION_KEY: ${{ secrets.LITESTREAM_B2_APPLICATION_KEY }}
LITESTREAM_B2_ENDPOINT: ${{ secrets.LITESTREAM_B2_ENDPOINT }}
LITESTREAM_B2_BUCKET: ${{ secrets.LITESTREAM_B2_BUCKET }}
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/b2-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: b2-test-results
path: |
*.log
test-results/
multipart-stress:
name: Multipart Upload Stress Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_multipart == 'true'
concurrency:
group: integration-test-multipart-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::Multipart stress tests skipped - S3 credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run multipart threshold tests (AWS S3)
if: steps.check-secrets.outputs.has_secrets == 'true'
run: |
go test -v ./replica_client_test.go -integration -replica-clients=s3 \
-run "TestReplicaClient_S3_Multipart|TestReplicaClient_S3_Concurrency" \
-timeout 30m
env:
LITESTREAM_S3_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}
LITESTREAM_S3_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_S3_SECRET_ACCESS_KEY }}
LITESTREAM_S3_REGION: us-east-1
LITESTREAM_S3_BUCKET: integration.litestream.io
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/multipart-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: multipart-test-results
path: |
*.log
test-results/
gcs-integration:
name: Google Cloud Storage Integration Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_gcs == 'true'
concurrency:
group: integration-test-gcp-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}" ]; then
echo "::notice title=Skipped::GCS integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- name: Extract GCP credentials
if: steps.check-secrets.outputs.has_secrets == 'true'
run: 'echo "$GOOGLE_APPLICATION_CREDENTIALS" > /opt/gcp.json'
shell: bash
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{secrets.GOOGLE_APPLICATION_CREDENTIALS}}
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run GCS integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=gs
env:
GOOGLE_APPLICATION_CREDENTIALS: /opt/gcp.json
LITESTREAM_GS_BUCKET: litestream-github-workflows
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/gcs-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: gcs-test-results
path: |
*.log
test-results/
abs-integration:
name: Azure Blob Storage Integration Tests
runs-on: ubuntu-latest
needs: setup
if: github.event.inputs.test_abs == 'true'
concurrency:
group: integration-test-abs-manual
cancel-in-progress: false
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_ABS_ACCOUNT_NAME }}" ]; then
echo "::notice title=Skipped::Azure Blob Storage integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
ref: ${{ needs.setup.outputs.ref }}
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Show Go environment
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go env
- name: Install litestream
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go install ./cmd/litestream
- name: Run Azure Blob Storage integration tests
if: steps.check-secrets.outputs.has_secrets == 'true'
run: go test -v ./replica_client_test.go -integration -replica-clients=abs
env:
LITESTREAM_ABS_ACCOUNT_NAME: ${{ secrets.LITESTREAM_ABS_ACCOUNT_NAME }}
LITESTREAM_ABS_ACCOUNT_KEY: ${{ secrets.LITESTREAM_ABS_ACCOUNT_KEY }}
LITESTREAM_ABS_BUCKET: integration
- name: Create test results directory
if: always()
run: |
mkdir -p test-results
echo "Test completed at $(date)" > test-results/abs-test.log
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: abs-test-results
path: |
*.log
test-results/
summary:
name: Test Summary
runs-on: ubuntu-latest
needs: [setup, s3-integration, gcs-integration, abs-integration, tigris-integration, r2-integration, b2-integration, multipart-stress]
if: always()
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
continue-on-error: true
- name: Generate summary
run: |
echo "## Integration Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Target: ${{ needs.setup.outputs.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Integration Tests" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.s3-integration.result }}" == "success" ]; then
echo "✅ **S3 (AWS):** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.s3-integration.result }}" == "failure" ]; then
echo "❌ **S3 (AWS):** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.s3-integration.result }}" == "skipped" ]; then
echo "⏭️ **S3 (AWS):** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.gcs-integration.result }}" == "success" ]; then
echo "✅ **Google Cloud Storage:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.gcs-integration.result }}" == "failure" ]; then
echo "❌ **Google Cloud Storage:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.gcs-integration.result }}" == "skipped" ]; then
echo "⏭️ **Google Cloud Storage:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.abs-integration.result }}" == "success" ]; then
echo "✅ **Azure Blob Storage:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.abs-integration.result }}" == "failure" ]; then
echo "❌ **Azure Blob Storage:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.abs-integration.result }}" == "skipped" ]; then
echo "⏭️ **Azure Blob Storage:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.tigris-integration.result }}" == "success" ]; then
echo "✅ **Fly.io Tigris:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.tigris-integration.result }}" == "failure" ]; then
echo "❌ **Fly.io Tigris:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.tigris-integration.result }}" == "skipped" ]; then
echo "⏭️ **Fly.io Tigris:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.r2-integration.result }}" == "success" ]; then
echo "✅ **Cloudflare R2:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.r2-integration.result }}" == "failure" ]; then
echo "❌ **Cloudflare R2:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.r2-integration.result }}" == "skipped" ]; then
echo "⏭️ **Cloudflare R2:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.b2-integration.result }}" == "success" ]; then
echo "✅ **Backblaze B2:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.b2-integration.result }}" == "failure" ]; then
echo "❌ **Backblaze B2:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.b2-integration.result }}" == "skipped" ]; then
echo "⏭️ **Backblaze B2:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.multipart-stress.result }}" == "success" ]; then
echo "✅ **Multipart Stress Tests:** Passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.multipart-stress.result }}" == "failure" ]; then
echo "❌ **Multipart Stress Tests:** Failed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.multipart-stress.result }}" == "skipped" ]; then
echo "⏭️ **Multipart Stress Tests:** Skipped" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "**Triggered by:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "**Run ID:** [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
echo "**Workflow:** ${{ github.workflow }}" >> $GITHUB_STEP_SUMMARY
- name: Comment on PR (if applicable)
if: github.event.inputs.pr_number != ''
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const pr_number = ${{ github.event.inputs.pr_number }};
const run_url = `https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
// Build status emoji based on results
let statusEmoji = '✅';
const failedJobs = [];
if ('${{ needs.s3-integration.result }}' === 'failure') failedJobs.push('S3');
if ('${{ needs.gcs-integration.result }}' === 'failure') failedJobs.push('GCS');
if ('${{ needs.abs-integration.result }}' === 'failure') failedJobs.push('Azure');
if ('${{ needs.tigris-integration.result }}' === 'failure') failedJobs.push('Tigris');
if ('${{ needs.r2-integration.result }}' === 'failure') failedJobs.push('R2');
if ('${{ needs.b2-integration.result }}' === 'failure') failedJobs.push('B2');
if ('${{ needs.multipart-stress.result }}' === 'failure') failedJobs.push('Multipart');
if (failedJobs.length > 0) {
statusEmoji = '❌';
}
let body = `${statusEmoji} **Manual integration tests** have been run by @${{ github.actor }}\n\n`;
if (failedJobs.length > 0) {
body += `Failed jobs: ${failedJobs.join(', ')}\n\n`;
}
body += `[View test results](${run_url})`;
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr_number,
body: body
});
console.log(`Successfully commented on PR #${pr_number}`);
} catch (error) {
console.log(`Failed to comment on PR #${pr_number}: ${error.message}`);
// Don't fail the workflow if commenting fails
}
================================================
FILE: .github/workflows/pr-metrics.yml
================================================
name: PR Build Metrics
on:
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: pr-metrics-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
jobs:
build-base:
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
- name: Set up Go
id: setup-go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Cache base binary
id: cache-base
uses: actions/cache@v4
with:
path: litestream-base
key: pr-metrics-base-${{ github.event.pull_request.base.sha }}-${{ steps.setup-go.outputs.go-version }}
- name: Build base binary
if: steps.cache-base.outputs.cache-hit != 'true'
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o litestream-base ./cmd/litestream
- name: Record base binary size
run: stat --format=%s litestream-base > base-size.txt
- name: Upload base size
uses: actions/upload-artifact@v4
with:
name: base-size
path: base-size.txt
build-pr:
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Build PR binary
run: |
START=$SECONDS
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o litestream-pr ./cmd/litestream
echo "$((SECONDS - START))" > build-time.txt
go version | awk '{print $3}' > go-version.txt
- name: Record PR binary size
run: stat --format=%s litestream-pr > pr-size.txt
- name: Upload PR size
uses: actions/upload-artifact@v4
with:
name: pr-size
path: pr-size.txt
- name: Upload build info
uses: actions/upload-artifact@v4
with:
name: build-info
path: |
build-time.txt
go-version.txt
analyze-deps:
runs-on: ubuntu-latest
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
path: pr
- name: Checkout base branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: pr/go.mod
- name: Diff go.mod dependencies
run: |
extract_require_deps() {
local gomod="$1"
awk '/^require \(/{found=1; next} found && /^\)/{found=0} found && /^\t/' "$gomod" | sed 's/^\t//' | sort
}
extract_require_deps base/go.mod > /tmp/base-deps.txt
extract_require_deps pr/go.mod > /tmp/pr-deps.txt
{
echo "## Added"
comm -13 /tmp/base-deps.txt /tmp/pr-deps.txt
echo "## Removed"
comm -23 /tmp/base-deps.txt /tmp/pr-deps.txt
} > deps-diff.txt
- name: Module graph size
run: |
cd base && go mod graph | wc -l | tr -d ' ' > ../base-graph-size.txt && cd ..
cd pr && go mod graph | wc -l | tr -d ' ' > ../pr-graph-size.txt && cd ..
- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
cd pr
govulncheck ./... > ../vulncheck-results.txt 2>&1 || true
- name: Check Go toolchain freshness
run: |
CURRENT=$(grep '^toolchain ' pr/go.mod | awk '{print $2}' | sed 's/^go//')
if [ -z "$CURRENT" ]; then
CURRENT=$(grep '^go ' pr/go.mod | awk '{print $2}')
fi
MINOR=$(echo "$CURRENT" | grep -oE '^[0-9]+\.[0-9]+')
LATEST=$(curl -sf 'https://go.dev/dl/?mode=json&include=all' | \
python3 -c "import sys,json; releases=json.load(sys.stdin); print(next(r['version'] for r in releases if r['stable'] and r['version'].startswith('go${MINOR}.')))" 2>/dev/null | sed 's/^go//')
if [ -z "$LATEST" ]; then
echo "current=${CURRENT}" > go-toolchain.txt
echo "latest=unknown" >> go-toolchain.txt
echo "stale=false" >> go-toolchain.txt
elif [ "$CURRENT" = "$LATEST" ]; then
echo "current=${CURRENT}" > go-toolchain.txt
echo "latest=${LATEST}" >> go-toolchain.txt
echo "stale=false" >> go-toolchain.txt
else
echo "current=${CURRENT}" > go-toolchain.txt
echo "latest=${LATEST}" >> go-toolchain.txt
echo "stale=true" >> go-toolchain.txt
fi
- name: Upload dependency analysis
uses: actions/upload-artifact@v4
with:
name: dep-analysis
path: |
deps-diff.txt
base-graph-size.txt
pr-graph-size.txt
vulncheck-results.txt
go-toolchain.txt
post-comment:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
needs: [build-base, build-pr, analyze-deps]
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Post PR comment and manage labels
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue_number = context.issue.number;
const baseSize = parseInt(fs.readFileSync('base-size/base-size.txt', 'utf8').trim());
const prSize = parseInt(fs.readFileSync('pr-size/pr-size.txt', 'utf8').trim());
const buildTime = fs.readFileSync('build-info/build-time.txt', 'utf8').trim();
const goVersion = fs.readFileSync('build-info/go-version.txt', 'utf8').trim();
const depsDiff = fs.readFileSync('dep-analysis/deps-diff.txt', 'utf8').trim();
const baseGraphSize = fs.readFileSync('dep-analysis/base-graph-size.txt', 'utf8').trim();
const prGraphSize = fs.readFileSync('dep-analysis/pr-graph-size.txt', 'utf8').trim();
let vulncheck = fs.readFileSync('dep-analysis/vulncheck-results.txt', 'utf8').trim();
const toolchainData = fs.readFileSync('dep-analysis/go-toolchain.txt', 'utf8').trim();
const MAX_VULNCHECK_LEN = 10000;
if (vulncheck.length > MAX_VULNCHECK_LEN) {
vulncheck = vulncheck.substring(0, MAX_VULNCHECK_LEN) + '\n... (truncated, see workflow run for full output)';
}
// --- Parse Go toolchain freshness ---
const tcLines = Object.fromEntries(toolchainData.split('\n').map(l => l.split('=')));
const goCurrent = tcLines.current || 'unknown';
const goLatest = tcLines.latest || 'unknown';
const goStale = tcLines.stale === 'true';
// --- Compute metrics ---
const diff = prSize - baseSize;
const absPct = baseSize > 0 ? Math.abs((diff / baseSize) * 100) : 0;
const pct = baseSize > 0 ? ((diff / baseSize) * 100).toFixed(2) : 'N/A';
const sign = diff > 0 ? '+' : '';
const fmt = (bytes) => (bytes / 1024 / 1024).toFixed(2) + ' MB';
const addedSection = depsDiff.split('## Removed')[0].replace('## Added', '').trim();
const removedSection = depsDiff.split('## Removed')[1]?.trim() || '';
const addedDeps = addedSection ? addedSection.split('\n').filter(l => l.trim()) : [];
const removedDeps = removedSection ? removedSection.split('\n').filter(l => l.trim()) : [];
const depsChanged = addedDeps.length + removedDeps.length;
const graphDiff = parseInt(prGraphSize) - parseInt(baseGraphSize);
const graphSign = graphDiff > 0 ? '+' : '';
const hasVulns = vulncheck.includes('Vulnerability #') || vulncheck.includes('GO-');
// --- Determine status ---
const flags = [];
let sizeIcon = '✅';
if (diff > 0 && absPct >= 10) { sizeIcon = '🚨'; flags.push('binary size >10%'); }
else if (diff > 0 && absPct >= 5) { sizeIcon = '⚠️'; flags.push('binary size >5%'); }
else if (diff < 0) { sizeIcon = '📉'; }
const vulnIcon = hasVulns ? '⚠️' : '✅';
if (hasVulns) flags.push('vulnerabilities found');
const goIcon = goStale ? '⚠️' : '✅';
if (goStale) flags.push(`Go toolchain outdated (${goCurrent} → ${goLatest})`);
const depsIcon = depsChanged > 0 ? 'ℹ️' : '✅';
const overallIcon = flags.length > 0 ? '⚠️' : '✅';
const overallText = flags.length > 0
? `**Attention needed** — ${flags.join(', ')}`
: '**All clear** — no issues detected';
// --- Build compact comment ---
let depsDetail = 'No dependency changes.';
if (depsChanged > 0) {
const parts = [];
if (addedDeps.length > 0) parts.push('**Added:**\n' + addedDeps.map(d => `- \`${d}\``).join('\n'));
if (removedDeps.length > 0) parts.push('**Removed:**\n' + removedDeps.map(d => `- \`${d}\``).join('\n'));
depsDetail = parts.join('\n\n');
}
const body = `## PR Build Metrics
${overallIcon} ${overallText}
| Check | Status | Summary |
|:------|:------:|:--------|
| Binary size | ${sizeIcon} | ${fmt(prSize)} (${sign}${(diff / 1024).toFixed(1)} KB / ${sign}${pct}%) |
| Dependencies | ${depsIcon} | ${depsChanged > 0 ? `${addedDeps.length} added, ${removedDeps.length} removed` : 'No changes'} |
| Vulnerabilities | ${vulnIcon} | ${hasVulns ? 'Issues found — expand details below' : 'None detected'} |
| Go toolchain | ${goIcon} | ${goCurrent}${goStale ? ` → ${goLatest} available` : ' (latest)'} |
| Module graph | ✅ | ${prGraphSize} edges (${graphSign}${graphDiff}) |
### Binary Size
| | Size | Change |
|---|---:|---:|
| Base (\`${context.payload.pull_request.base.sha.substring(0, 7)}\`) | ${fmt(baseSize)} | |
| PR (\`${context.sha.substring(0, 7)}\`) | ${fmt(prSize)} | ${sign}${(diff / 1024).toFixed(1)} KB (${sign}${pct}%) |
### Dependency Changes
${depsDetail}
### govulncheck Output
\`\`\`
${vulncheck}
\`\`\`
### Build Info
| Metric | Value |
|---|---|
| Build time | ${buildTime}s |
| Go version | \`${goVersion}\` |
| Commit | \`${context.sha.substring(0, 7)}\` |
<!-- history -->
---
<sub>🤖 Updated on each push.</sub>`.replace(/^ /gm, '');
// --- Post or update comment (with history) ---
const marker = '## PR Build Metrics';
const historyMarker = '<!-- history -->';
let existing = null;
for await (const response of github.paginate.iterator(
github.rest.issues.listComments,
{ owner, repo, issue_number, per_page: 100 }
)) {
existing = response.data.find(c => c.body?.startsWith(marker));
if (existing) break;
}
if (existing) {
// Extract previous summary line and history from existing comment
const prevBody = existing.body;
const prevStatusMatch = prevBody.match(/\| Binary size \|[^\n]+/);
const prevCommitMatch = prevBody.match(/\| Commit \| `([^`]+)` \|/);
const prevSummary = prevStatusMatch ? prevStatusMatch[0] : null;
const prevCommit = prevCommitMatch ? prevCommitMatch[1] : '?';
// Extract existing history entries
let historyEntries = '';
const historyIdx = prevBody.indexOf(historyMarker);
if (historyIdx !== -1) {
const afterMarker = prevBody.substring(historyIdx + historyMarker.length);
// Support both old <details> format and new plain format
const detailsMatch = afterMarker.match(/<details>[\s\S]*?<\/details>/);
if (detailsMatch) {
const innerMatch = detailsMatch[0].match(/<summary>[^<]*<\/summary>([\s\S]*?)<\/details>/);
if (innerMatch) historyEntries = innerMatch[1].trim();
} else {
// Plain format: extract table rows after the header
const tableMatch = afterMarker.match(/\| Commit \| Updated[\s\S]*?(?=\n---|\n$|$)/);
if (tableMatch) historyEntries = tableMatch[0].trim();
}
}
// Build new history (most recent first, cap at 10)
const now = new Date().toISOString().replace('T', ' ').substring(0, 16) + ' UTC';
const newEntry = prevSummary
? `| \`${prevCommit}\` | ${now} | ${prevSummary.replace(/\| Binary size \|/, '').trim().replace(/^\||\|$/g, '').trim()} |`
: null;
let historyRows = '';
if (newEntry || historyEntries) {
const existingRows = historyEntries
.split('\n')
.filter(l => l.startsWith('|') && !l.startsWith('| Commit') && !l.startsWith('|:'))
.slice(0, 9);
const allRows = newEntry ? [newEntry, ...existingRows] : existingRows;
if (allRows.length > 0) {
historyRows = `\n### History (${allRows.length} previous)\n\n| Commit | Updated | Status | Summary |\n|:-------|:--------|:------:|:--------|\n${allRows.join('\n')}`;
}
}
// Insert history into body
const finalBody = body.replace(historyMarker, historyMarker + historyRows);
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: finalBody });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
// --- Manage labels ---
const LABELS = {
SIZE_WARNING: 'metrics: size-warning',
SIZE_ALERT: 'metrics: size-alert',
VULNS: 'metrics: vulns-found',
GO_UPDATE: 'metrics: go-update',
};
const ensureLabel = async (name, color, description) => {
try {
await github.rest.issues.getLabel({ owner, repo, name });
} catch {
await github.rest.issues.createLabel({ owner, repo, name, color, description });
}
};
const addLabel = async (name) => {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [name] });
};
const removeLabel = async (name) => {
try {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name });
} catch { /* label not present, ignore */ }
};
// Size labels
await ensureLabel(LABELS.SIZE_WARNING, 'fbca04', 'Binary size increased 5-10%');
await ensureLabel(LABELS.SIZE_ALERT, 'e11d48', 'Binary size increased >10%');
await ensureLabel(LABELS.VULNS, 'e11d48', 'govulncheck found vulnerabilities');
await ensureLabel(LABELS.GO_UPDATE, 'fbca04', 'Go toolchain has a newer patch release');
if (diff > 0 && absPct >= 10) {
await addLabel(LABELS.SIZE_ALERT);
await removeLabel(LABELS.SIZE_WARNING);
} else if (diff > 0 && absPct >= 5) {
await addLabel(LABELS.SIZE_WARNING);
await removeLabel(LABELS.SIZE_ALERT);
} else {
await removeLabel(LABELS.SIZE_WARNING);
await removeLabel(LABELS.SIZE_ALERT);
}
// Vuln label
if (hasVulns) {
await addLabel(LABELS.VULNS);
} else {
await removeLabel(LABELS.VULNS);
}
// Go toolchain label
if (goStale) {
await addLabel(LABELS.GO_UPDATE);
} else {
await removeLabel(LABELS.GO_UPDATE);
}
================================================
FILE: .github/workflows/pre-release-checklist.yml
================================================
name: Pre-Release Checklist
# Advisory workflow to verify release readiness
# This workflow reports results but does NOT block releases
# Run manually before tagging a new release
permissions:
contents: read
issues: write
pull-requests: write
on:
workflow_dispatch:
inputs:
version:
description: 'Version being released (e.g., v0.5.6)'
required: true
type: string
create_issue:
description: 'Create a GitHub issue with results'
required: false
default: true
type: boolean
test_cloud_providers:
description: 'Run cloud provider integration tests (S3, GCS, ABS, R2)'
required: false
default: true
type: boolean
test_multipart:
description: 'Run multipart upload stress tests'
required: false
default: true
type: boolean
jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
outputs:
result: ${{ steps.test.outcome }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Run unit tests
id: test
run: go test -v -race ./...
build-verification:
name: Build Verification
runs-on: ubuntu-latest
outputs:
result: ${{ steps.build.outcome }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Build main binary
id: build
run: |
go build -o bin/litestream ./cmd/litestream
./bin/litestream version
- name: Verify binary runs
run: |
./bin/litestream --help
./bin/litestream databases --help
./bin/litestream replicate --help
./bin/litestream restore --help
s3-integration:
name: S3 Integration (AWS)
runs-on: ubuntu-latest
if: github.event.inputs.test_cloud_providers == 'true'
outputs:
result: ${{ steps.test.outcome }}
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::S3 integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Run S3 integration tests
id: test
if: steps.check-secrets.outputs.has_secrets == 'true'
continue-on-error: true
run: go test -v ./replica_client_test.go -integration -replica-clients=s3
env:
LITESTREAM_S3_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}
LITESTREAM_S3_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_S3_SECRET_ACCESS_KEY }}
LITESTREAM_S3_REGION: us-east-1
LITESTREAM_S3_BUCKET: integration.litestream.io
gcs-integration:
name: GCS Integration
runs-on: ubuntu-latest
if: github.event.inputs.test_cloud_providers == 'true'
outputs:
result: ${{ steps.test.outcome }}
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}" ]; then
echo "::notice title=Skipped::GCS integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- name: Extract GCP credentials
if: steps.check-secrets.outputs.has_secrets == 'true'
run: 'echo "$GOOGLE_APPLICATION_CREDENTIALS" > /opt/gcp.json'
shell: bash
env:
GOOGLE_APPLICATION_CREDENTIALS: ${{secrets.GOOGLE_APPLICATION_CREDENTIALS}}
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Run GCS integration tests
id: test
if: steps.check-secrets.outputs.has_secrets == 'true'
continue-on-error: true
run: go test -v ./replica_client_test.go -integration -replica-clients=gs
env:
GOOGLE_APPLICATION_CREDENTIALS: /opt/gcp.json
LITESTREAM_GS_BUCKET: litestream-github-workflows
abs-integration:
name: Azure Blob Storage Integration
runs-on: ubuntu-latest
if: github.event.inputs.test_cloud_providers == 'true'
outputs:
result: ${{ steps.test.outcome }}
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_ABS_ACCOUNT_NAME }}" ]; then
echo "::notice title=Skipped::Azure Blob Storage integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Run ABS integration tests
id: test
if: steps.check-secrets.outputs.has_secrets == 'true'
continue-on-error: true
run: go test -v ./replica_client_test.go -integration -replica-clients=abs
env:
LITESTREAM_ABS_ACCOUNT_NAME: ${{ secrets.LITESTREAM_ABS_ACCOUNT_NAME }}
LITESTREAM_ABS_ACCOUNT_KEY: ${{ secrets.LITESTREAM_ABS_ACCOUNT_KEY }}
LITESTREAM_ABS_BUCKET: integration
r2-integration:
name: Cloudflare R2 Integration
runs-on: ubuntu-latest
if: github.event.inputs.test_cloud_providers == 'true'
outputs:
result: ${{ steps.test.outcome }}
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_R2_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::R2 integration tests skipped - credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Run R2 integration tests
id: test
if: steps.check-secrets.outputs.has_secrets == 'true'
continue-on-error: true
run: go test -v ./replica_client_test.go -integration -replica-clients=r2
env:
LITESTREAM_R2_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_R2_ACCESS_KEY_ID }}
LITESTREAM_R2_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_R2_SECRET_ACCESS_KEY }}
LITESTREAM_R2_ENDPOINT: ${{ secrets.LITESTREAM_R2_ENDPOINT }}
LITESTREAM_R2_BUCKET: ${{ secrets.LITESTREAM_R2_BUCKET }}
multipart-stress:
name: Multipart Upload Stress Tests
runs-on: ubuntu-latest
if: github.event.inputs.test_multipart == 'true'
outputs:
result: ${{ steps.test.outcome }}
steps:
- name: Check for required secrets
id: check-secrets
run: |
if [ -z "${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}" ]; then
echo "::notice title=Skipped::Multipart stress tests skipped - S3 credentials not configured"
echo "has_secrets=false" >> $GITHUB_OUTPUT
else
echo "has_secrets=true" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
if: steps.check-secrets.outputs.has_secrets == 'true'
- uses: actions/setup-go@v5
if: steps.check-secrets.outputs.has_secrets == 'true'
with:
go-version-file: "go.mod"
- name: Run multipart stress tests
id: test
if: steps.check-secrets.outputs.has_secrets == 'true'
continue-on-error: true
run: |
go test -v ./replica_client_test.go -integration -replica-clients=s3 \
-run "TestReplicaClient_S3_Multipart|TestReplicaClient_S3_Concurrency" \
-timeout 30m
env:
LITESTREAM_S3_ACCESS_KEY_ID: ${{ secrets.LITESTREAM_S3_ACCESS_KEY_ID }}
LITESTREAM_S3_SECRET_ACCESS_KEY: ${{ secrets.LITESTREAM_S3_SECRET_ACCESS_KEY }}
LITESTREAM_S3_REGION: us-east-1
LITESTREAM_S3_BUCKET: integration.litestream.io
generate-checklist:
name: Generate Release Checklist
runs-on: ubuntu-latest
needs:
- unit-tests
- build-verification
- s3-integration
- gcs-integration
- abs-integration
- r2-integration
- multipart-stress
if: always()
steps:
- name: Generate checklist summary
id: checklist
run: |
echo "## Pre-Release Checklist for ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
echo "**Triggered by:** @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Core Tests" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.unit-tests.result }}" == "success" ]; then
echo "- [x] Unit tests passed" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Unit tests **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.build-verification.result }}" == "success" ]; then
echo "- [x] Build verification passed" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Build verification **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Cloud Provider Integration Tests" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.s3-integration.result }}" == "success" ]; then
echo "- [x] AWS S3 integration passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.s3-integration.result }}" == "skipped" ]; then
echo "- [ ] AWS S3 integration (skipped)" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] AWS S3 integration **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.gcs-integration.result }}" == "success" ]; then
echo "- [x] Google Cloud Storage integration passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.gcs-integration.result }}" == "skipped" ]; then
echo "- [ ] Google Cloud Storage integration (skipped)" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Google Cloud Storage integration **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.abs-integration.result }}" == "success" ]; then
echo "- [x] Azure Blob Storage integration passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.abs-integration.result }}" == "skipped" ]; then
echo "- [ ] Azure Blob Storage integration (skipped)" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Azure Blob Storage integration **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.r2-integration.result }}" == "success" ]; then
echo "- [x] Cloudflare R2 integration passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.r2-integration.result }}" == "skipped" ]; then
echo "- [ ] Cloudflare R2 integration (skipped)" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Cloudflare R2 integration **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Stress Tests" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.multipart-stress.result }}" == "success" ]; then
echo "- [x] Multipart upload stress tests passed" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.multipart-stress.result }}" == "skipped" ]; then
echo "- [ ] Multipart upload stress tests (skipped)" >> $GITHUB_STEP_SUMMARY
else
echo "- [ ] Multipart upload stress tests **FAILED**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note:** This checklist is advisory only." >> $GITHUB_STEP_SUMMARY
echo "Review failed items before proceeding with the release." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[View full run details](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
# Create issue body for later use
cat > /tmp/issue_body.md << 'ISSUE_EOF'
## Pre-Release Checklist for ${{ github.event.inputs.version }}
**Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')
**Triggered by:** @${{ github.actor }}
**Workflow Run:** https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
### Core Tests
| Test | Status |
|------|--------|
| Unit Tests | ${{ needs.unit-tests.result == 'success' && '✅ Passed' || '❌ Failed' }} |
| Build Verification | ${{ needs.build-verification.result == 'success' && '✅ Passed' || '❌ Failed' }} |
### Cloud Provider Integration Tests
| Provider | Status |
|----------|--------|
| AWS S3 | ${{ needs.s3-integration.result == 'success' && '✅ Passed' || needs.s3-integration.result == 'skipped' && '⏭️ Skipped' || '❌ Failed' }} |
| Google Cloud Storage | ${{ needs.gcs-integration.result == 'success' && '✅ Passed' || needs.gcs-integration.result == 'skipped' && '⏭️ Skipped' || '❌ Failed' }} |
| Azure Blob Storage | ${{ needs.abs-integration.result == 'success' && '✅ Passed' || needs.abs-integration.result == 'skipped' && '⏭️ Skipped' || '❌ Failed' }} |
| Cloudflare R2 | ${{ needs.r2-integration.result == 'success' && '✅ Passed' || needs.r2-integration.result == 'skipped' && '⏭️ Skipped' || '❌ Failed' }} |
### Stress Tests
| Test | Status |
|------|--------|
| Multipart Uploads | ${{ needs.multipart-stress.result == 'success' && '✅ Passed' || needs.multipart-stress.result == 'skipped' && '⏭️ Skipped' || '❌ Failed' }} |
---
**Note:** This checklist is advisory only. Review any failed items before proceeding with the release.
ISSUE_EOF
- name: Create GitHub Issue
if: github.event.inputs.create_issue == 'true'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const version = '${{ github.event.inputs.version }}';
const runUrl = `https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
// Determine overall status
const results = {
unit: '${{ needs.unit-tests.result }}',
build: '${{ needs.build-verification.result }}',
s3: '${{ needs.s3-integration.result }}',
gcs: '${{ needs.gcs-integration.result }}',
abs: '${{ needs.abs-integration.result }}',
r2: '${{ needs.r2-integration.result }}',
multipart: '${{ needs.multipart-stress.result }}'
};
const failed = Object.entries(results)
.filter(([_, v]) => v === 'failure')
.map(([k, _]) => k);
const status = failed.length === 0 ? '✅' : '⚠️';
const title = `${status} Pre-Release Checklist: ${version}`;
let body = `## Pre-Release Checklist for ${version}\n\n`;
body += `**Workflow Run:** [View Details](${runUrl})\n\n`;
body += `### Results Summary\n\n`;
body += `| Test | Status |\n`;
body += `|------|--------|\n`;
body += `| Unit Tests | ${results.unit === 'success' ? '✅' : '❌'} |\n`;
body += `| Build | ${results.build === 'success' ? '✅' : '❌'} |\n`;
body += `| AWS S3 | ${results.s3 === 'success' ? '✅' : results.s3 === 'skipped' ? '⏭️' : '❌'} |\n`;
body += `| GCS | ${results.gcs === 'success' ? '✅' : results.gcs === 'skipped' ? '⏭️' : '❌'} |\n`;
body += `| Azure | ${results.abs === 'success' ? '✅' : results.abs === 'skipped' ? '⏭️' : '❌'} |\n`;
body += `| R2 | ${results.r2 === 'success' ? '✅' : results.r2 === 'skipped' ? '⏭️' : '❌'} |\n`;
body += `| Multipart | ${results.multipart === 'success' ? '✅' : results.multipart === 'skipped' ? '⏭️' : '❌'} |\n\n`;
if (failed.length > 0) {
body += `### ⚠️ Failed Tests\n\n`;
body += `The following tests failed and should be reviewed:\n`;
failed.forEach(f => body += `- ${f}\n`);
body += `\n`;
}
body += `---\n`;
body += `*This issue was automatically created by the pre-release checklist workflow.*\n`;
try {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: body,
labels: ['release-checklist']
});
console.log('Created release checklist issue');
} catch (error) {
console.log(`Failed to create issue: ${error.message}`);
}
================================================
FILE: .github/workflows/release.docker.yml
================================================
on:
release:
types:
- published
# pull_request:
# types:
# - opened
# - synchronize
# - reopened
# branches-ignore:
# - "dependabot/**"
name: Release (Docker)
jobs:
docker:
runs-on: ubuntu-latest
env:
# CGO cross-compilation supported platforms
PLATFORMS: "linux/amd64,linux/arm64"
VERSION: "${{ github.event_name == 'release' && github.event.release.tag_name || github.sha }}"
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: benbjohnson
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Default (Debian) image
- id: meta-default
uses: docker/metadata-action@v5
with:
images: litestream/litestream
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- uses: docker/build-push-action@v6
with:
context: .
target: default
push: true
provenance: true
sbom: true
platforms: ${{ env.PLATFORMS }}
tags: ${{ steps.meta-default.outputs.tags }}
labels: ${{ steps.meta-default.outputs.labels }}
build-args: |
LITESTREAM_VERSION=${{ env.VERSION }}
# Hardened (Scratch) image
- id: meta-scratch
uses: docker/metadata-action@v5
with:
images: litestream/litestream
flavor: |
latest=auto
suffix=-scratch
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha
type=sha,format=long
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- uses: docker/build-push-action@v6
with:
context: .
target: hardened
push: true
provenance: true
sbom: true
platforms: ${{ env.PLATFORMS }}
tags: ${{ steps.meta-scratch.outputs.tags }}
labels: ${{ steps.meta-scratch.outputs.labels }}
build-args: |
LITESTREAM_VERSION=${{ env.VERSION }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g., v0.3.14)'
required: true
type: string
permissions:
contents: write
packages: write
id-token: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Install cross-compilers
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf gcc-arm-linux-gnueabi
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Install Syft for SBOM generation
uses: anchore/sbom-action/download-syft@v0
with:
syft-version: latest
- name: Import GPG key
if: ${{ env.GPG_PRIVATE_KEY != '' }}
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
vfs-build-linux:
name: Build VFS (Linux ${{ matrix.arch }})
runs-on: ubuntu-22.04 # glibc 2.35 (ubuntu-20.04 runners deprecated)
needs: goreleaser
strategy:
matrix:
arch: [amd64, arm64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Install cross-compiler (arm64)
if: matrix.arch == 'arm64'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "version=${{ inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Build VFS extension
run: make vfs-linux-${{ matrix.arch }}
- name: Verify artifact
run: |
file dist/litestream-vfs-linux-${{ matrix.arch }}.so
# For native arch, verify it can be loaded by SQLite
if [ "${{ matrix.arch }}" == "amd64" ]; then
sqlite3 ':memory:' ".load dist/litestream-vfs-linux-amd64.so" ".exit" && echo "Extension loads successfully"
fi
# For cross-compiled arch, verify ELF header and architecture
if [ "${{ matrix.arch }}" == "arm64" ]; then
readelf -h dist/litestream-vfs-linux-arm64.so | grep -E "(Class|Machine)"
echo "ARM64 ELF header verified"
fi
- name: Create archive
run: |
cd dist
cp litestream-vfs-linux-${{ matrix.arch }}.so litestream.so
tar -czvf litestream-vfs-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz \
litestream.so
- name: Generate checksum
run: |
cd dist
sha256sum litestream-vfs-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz \
> litestream-vfs-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz.sha256
- name: Upload to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ steps.version.outputs.version }} \
dist/litestream-vfs-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz \
dist/litestream-vfs-${{ steps.version.outputs.version }}-linux-${{ matrix.arch }}.tar.gz.sha256 \
--clobber
- name: Upload artifact for packaging
uses: actions/upload-artifact@v4
with:
name: vfs-linux-${{ matrix.arch }}
path: dist/litestream-vfs-linux-${{ matrix.arch }}.so
vfs-build-darwin:
name: Build VFS (macOS ${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
needs: goreleaser
strategy:
matrix:
include:
- arch: amd64
runner: macos-15
- arch: arm64
runner: macos-15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "version=${{ inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Build VFS extension
run: make vfs-darwin-${{ matrix.arch }}
- name: Verify artifact
run: |
file dist/litestream-vfs-darwin-${{ matrix.arch }}.dylib
# Verify architecture matches target
lipo -info dist/litestream-vfs-darwin-${{ matrix.arch }}.dylib
# Verify the expected symbol exists (macOS sqlite3 doesn't support .load)
nm -gU dist/litestream-vfs-darwin-${{ matrix.arch }}.dylib | grep sqlite3_litestreamvfs_init && echo "Entry point symbol found"
- name: Create archive
run: |
cd dist
cp litestream-vfs-darwin-${{ matrix.arch }}.dylib litestream.dylib
tar -czvf litestream-vfs-${{ steps.version.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz \
litestream.dylib
- name: Generate checksum
run: |
cd dist
shasum -a 256 litestream-vfs-${{ steps.version.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz \
> litestream-vfs-${{ steps.version.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz.sha256
- name: Upload to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ steps.version.outputs.version }} \
dist/litestream-vfs-${{ steps.version.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz \
dist/litestream-vfs-${{ steps.version.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz.sha256 \
--clobber
- name: Upload artifact for packaging
uses: actions/upload-artifact@v4
with:
name: vfs-darwin-${{ matrix.arch }}
path: dist/litestream-vfs-darwin-${{ matrix.arch }}.dylib
# macos-sign:
# runs-on: macos-latest
# needs: goreleaser
# strategy:
# matrix:
# arch: [amd64, arm64]
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Set up Go
# uses: actions/setup-go@v5
# with:
# go-version-file: go.mod
# - name: Download release artifacts
# uses: actions/download-artifact@v4
# with:
# name: litestream-darwin-${{ matrix.arch }}
# path: dist/
# - name: Import Apple Developer Certificate
# env:
# MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE_P12 }}
# MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
# run: |
# echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12
# security create-keychain -p actions temp.keychain
# security default-keychain -s temp.keychain
# security unlock-keychain -p actions temp.keychain
# security import certificate.p12 -k temp.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
# security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k actions temp.keychain
# - name: Sign and Notarize
# env:
# APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_P8 }}
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
# APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
# AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
# run: |
# gon etc/gon-${{ matrix.arch }}.hcl
# - name: Upload signed binary
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: |
# gh release upload ${{ github.ref_name }} dist/litestream-*-darwin-${{ matrix.arch }}.zip
# windows-sign:
# runs-on: windows-latest
# needs: goreleaser
# strategy:
# matrix:
# arch: [amd64, arm64]
# steps:
# - name: Checkout
# uses: actions/checkout@v4
#
# - name: Download release artifacts
# uses: actions/download-artifact@v4
# with:
# name: litestream-windows-${{ matrix.arch }}
# path: dist/
#
# - name: Sign Windows binary
# env:
# WINDOWS_CERTIFICATE_PFX: ${{ secrets.WINDOWS_CERTIFICATE_PFX }}
# WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
# run: |
# echo "$env:WINDOWS_CERTIFICATE_PFX" | base64 -d > cert.pfx
# & signtool sign /f cert.pfx /p "$env:WINDOWS_CERTIFICATE_PASSWORD" /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com dist\litestream.exe
# Remove-Item cert.pfx
#
# - name: Upload signed binary
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: |
# gh release upload ${{ github.ref_name }} dist\litestream-*-windows-${{ matrix.arch }}.zip
publish-pypi:
name: Publish to PyPI (${{ matrix.os }}-${{ matrix.arch }})
runs-on: ubuntu-latest
needs: [vfs-build-linux, vfs-build-darwin]
environment: release
permissions:
id-token: write
strategy:
matrix:
include:
- os: linux
arch: amd64
ext: so
src_name: litestream-vfs-linux-amd64.so
dest_name: litestream-vfs.so
platform_tag: manylinux_2_35_x86_64
- os: linux
arch: arm64
ext: so
src_name: litestream-vfs-linux-arm64.so
dest_name: litestream-vfs.so
platform_tag: manylinux_2_35_aarch64
- os: darwin
arch: amd64
ext: dylib
src_name: litestream-vfs-darwin-amd64.dylib
dest_name: litestream-vfs.dylib
platform_tag: macosx_11_0_x86_64
- os: darwin
arch: arm64
ext: dylib
src_name: litestream-vfs-darwin-arm64.dylib
dest_name: litestream-vfs.dylib
platform_tag: macosx_11_0_arm64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Download VFS artifact
uses: actions/download-artifact@v4
with:
name: vfs-${{ matrix.os }}-${{ matrix.arch }}
path: packages/python/litestream_vfs/
- name: Rename binary
run: |
mv packages/python/litestream_vfs/${{ matrix.src_name }} \
packages/python/litestream_vfs/${{ matrix.dest_name }}
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION="${{ inputs.tag }}"
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
- name: Build wheel
run: |
cd packages/python
pip install setuptools wheel
LITESTREAM_VERSION=${{ steps.version.outputs.version }} python setup.py bdist_wheel
- name: Rename wheel with platform tag
run: |
cd packages/python
python scripts/rename_wheel.py dist ${{ matrix.platform_tag }}
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: packages/python/dist/
publish-npm:
name: Publish to npm
runs-on: ubuntu-latest
needs: [vfs-build-linux, vfs-build-darwin]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
- name: Download all VFS artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Copy binaries to platform packages
run: |
cp artifacts/vfs-linux-amd64/litestream-vfs-linux-amd64.so \
packages/npm/litestream-vfs-linux-amd64/litestream-vfs.so
cp artifacts/vfs-linux-arm64/litestream-vfs-linux-arm64.so \
packages/npm/litestream-vfs-linux-arm64/litestream-vfs.so
cp artifacts/vfs-darwin-amd64/litestream-vfs-darwin-amd64.dylib \
packages/npm/litestream-vfs-darwin-amd64/litestream-vfs.dylib
cp artifacts/vfs-darwin-arm64/litestream-vfs-darwin-arm64.dylib \
packages/npm/litestream-vfs-darwin-arm64/litestream-vfs.dylib
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION="${{ inputs.tag }}"
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
- name: Set versions
run: |
VERSION=${{ steps.version.outputs.version }}
for dir in packages/npm/litestream-vfs-*/; do
jq --arg v "$VERSION" '.version = $v' "$dir/package.json" > tmp.json && mv tmp.json "$dir/package.json"
done
jq --arg v "$VERSION" '
.version = $v |
.optionalDependencies = (.optionalDependencies | to_entries | map(.value = $v) | from_entries)
' packages/npm/litestream-vfs/package.json > tmp.json && mv tmp.json packages/npm/litestream-vfs/package.json
- name: Publish platform packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
for dir in packages/npm/litestream-vfs-*/; do
cd "$dir"
npm publish --access public
cd -
done
- name: Wait for registry propagation
run: sleep 30
- name: Publish main package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cd packages/npm/litestream-vfs
npm publish --access public
publish-gem:
name: Publish to RubyGems (${{ matrix.os }}-${{ matrix.arch }})
runs-on: ubuntu-latest
needs: [vfs-build-linux, vfs-build-darwin]
strategy:
matrix:
include:
- os: linux
arch: amd64
ext: so
src_name: litestream-vfs-linux-amd64.so
dest_name: litestream-vfs.so
platform: x86_64-linux
- os: linux
arch: arm64
ext: so
src_name: litestream-vfs-linux-arm64.so
dest_name: litestream-vfs.so
platform: aarch64-linux
- os: darwin
arch: amd64
ext: dylib
src_name: litestream-vfs-darwin-amd64.dylib
dest_name: litestream-vfs.dylib
platform: x86_64-darwin
- os: darwin
arch: arm64
ext: dylib
src_name: litestream-vfs-darwin-arm64.dylib
dest_name: litestream-vfs.dylib
platform: arm64-darwin
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
- name: Download VFS artifact
uses: actions/download-artifact@v4
with:
name: vfs-${{ matrix.os }}-${{ matrix.arch }}
path: packages/ruby/lib/
- name: Rename binary
run: |
mv packages/ruby/lib/${{ matrix.src_name }} \
packages/ruby/lib/${{ matrix.dest_name }}
- name: Get version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
VERSION="${{ inputs.tag }}"
else
VERSION="${GITHUB_REF#refs/tags/}"
fi
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
- name: Build gem
run: |
cd packages/ruby
LITESTREAM_VERSION=${{ steps.version.outputs.version }} \
PLATFORM=${{ matrix.platform }} \
gem build litestream-vfs.gemspec
- name: Publish to RubyGems
env:
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
run: |
cd packages/ruby
gem push litestream-vfs-*.gem
================================================
FILE: .github/workflows/stale-issues.yml
================================================
name: Stale Issue Manager
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Issues: Label after 90 days, close after 30 more days
days-before-stale: 90
days-before-close: 30
stale-issue-label: 'stale'
# Exempt these labels from stale marking
exempt-issue-labels: 'Soon,release-blocker'
# Message posted when issue becomes stale
stale-issue-message: >
This issue has been inactive for 90 days and will be automatically
closed in 30 days if there is no further activity. If this issue is
still relevant, please add a comment to keep it open. Thank you for
your contribution!
# Message posted when issue is closed
close-issue-message: >
This issue has been automatically closed due to inactivity. If you
believe this issue is still relevant, please reopen it or create a
new issue with updated information. Thank you!
# Disable pull request processing
days-before-pr-stale: -1
days-before-pr-close: -1
# Limit operations per run
operations-per-run: 100
# Remove stale label when there is activity
remove-stale-when-updated: true
================================================
FILE: .github/workflows/upgrade-tests.yml
================================================
name: Upgrade Tests
on:
pull_request:
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- 'tests/integration/**'
- '.github/workflows/upgrade-tests.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
upgrade-test:
name: v0.3.x to v0.5.x Upgrade Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: Download Litestream v0.3.13
run: |
mkdir -p /tmp/litestream-v3
gh release download v0.3.13 --repo benbjohnson/litestream --pattern 'litestream-v0.3.13-linux-amd64.tar.gz' --dir /tmp
tar -xzf /tmp/litestream-v0.3.13-linux-amd64.tar.gz -C /tmp/litestream-v3
chmod +x /tmp/litestream-v3/litestream
/tmp/litestream-v3/litestream version
env:
GH_TOKEN: ${{ github.token }}
- name: Build current binaries
run: go build -o bin/litestream ./cmd/litestream && go build -o bin/litestream-test ./cmd/litestream-test
- name: Run upgrade integration tests
run: go test -v -tags=integration -timeout=10m ./tests/integration/... -run=TestUpgrade
env:
CGO_ENABLED: "1"
LITESTREAM_V3_BIN: /tmp/litestream-v3/litestream
- name: Upload test logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: upgrade-test-logs
path: /tmp/litestream-*/*.log
================================================
FILE: .gitignore
================================================
.DS_Store
/src/litestream-vfs.h
/dist
.vscode
.sprite
# Claude-related files (force include despite global gitignore)
!.claude/
!.claude/**
# But ignore logs, hooks, and local settings
.claude/logs/
.claude/hooks/
.claude/settings.local.json
# Keep CLAUDE.md ignored as it's auto-loaded by Claude Code
CLAUDE.md
# Binary
bin/
# Cached binaries for integration tests
.cache/
.claude/*.loop.local.md
.claude/worktrees/
CLAUDE.local.md
# Package manager build artifacts
packages/python/dist/
packages/python/build/
packages/python/*.egg-info/
================================================
FILE: .goreleaser.yml
================================================
version: 2
project_name: litestream
before:
hooks:
- go mod tidy
builds:
- id: litestream
main: ./cmd/litestream
binary: litestream
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
- arm
goarm:
- "6"
- "7"
ldflags:
- -s -w -X main.Version={{.Version}}
ignore:
- goos: windows
goarch: arm
- goos: darwin
goarch: arm
archives:
- id: main
formats:
- tar.gz
format_overrides:
- goos: windows
formats:
- zip
name_template: >-
{{ .ProjectName }}-
{{- .Version }}-
{{- .Os }}-
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
files:
- etc/litestream.yml
- etc/litestream.service
- README.md
- LICENSE
nfpms:
- vendor: Litestream
homepage: https://litestream.io
maintainer: Litestream Contributors <benbjohnson@yahoo.com>
description: Streaming replication for SQLite databases
license: Apache 2.0
formats:
- deb
- rpm
contents:
- src: etc/litestream.yml
dst: /etc/litestream.yml
type: config
- src: etc/litestream.service
dst: /lib/systemd/system/litestream.service
type: config
bindir: /usr/bin
file_name_template: >-
{{ .ProjectName }}-
{{- .Version }}-
{{- .Os }}-
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
brews:
- name: litestream
homepage: https://litestream.io
description: Streaming replication for SQLite databases
license: Apache-2.0
repository:
owner: benbjohnson
name: homebrew-litestream
branch: main
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
install: |
bin.install "litestream"
etc.install "etc/litestream.yml" => "litestream.yml"
test: |
system "#{bin}/litestream", "version"
commit_author:
name: goreleaser
email: bot@goreleaser.com
checksum:
name_template: 'checksums.txt'
algorithm: sha256
snapshot:
version_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore:'
- 'Merge pull request'
- 'Merge branch'
release:
github:
owner: benbjohnson
name: litestream
draft: false
prerelease: auto
mode: replace
header: |
## Platform Support
⚠️ **Windows Notice**: Windows binaries are provided for convenience but Windows is NOT an officially supported platform. Use at your own risk. Community contributions for Windows improvements are welcome.
✅ **Supported Platforms**: Linux (amd64, arm64, armv6, armv7), macOS (amd64, arm64)
## Installation
### Homebrew (macOS and Linux)
```bash
brew tap benbjohnson/litestream
brew install litestream
```
### Debian/Ubuntu
Download the `.deb` file for your architecture and install:
```bash
sudo dpkg -i litestream-*.deb
```
### RPM-based systems
Download the `.rpm` file for your architecture and install:
```bash
sudo rpm -i litestream-*.rpm
```
### Binary installation
Download the appropriate archive for your platform, extract, and move to your PATH.
## VFS Extension (Experimental)
SQLite loadable extensions for read-only access to Litestream replicas are available for supported platforms:
| Platform | File |
|----------|------|
| Linux x86_64 | `litestream-vfs-v{{.Version}}-linux-amd64.tar.gz` |
| Linux ARM64 | `litestream-vfs-v{{.Version}}-linux-arm64.tar.gz` |
| macOS Intel | `litestream-vfs-v{{.Version}}-darwin-amd64.tar.gz` |
| macOS Apple Silicon | `litestream-vfs-v{{.Version}}-darwin-arm64.tar.gz` |
Install via package managers:
```bash
pip install litestream-vfs # Python
npm install litestream-vfs # Node.js
gem install litestream-vfs # Ruby
```
# Signing configuration
# signs:
# - id: macos
# cmd: gon
# args:
# - "{{ .ProjectPath }}/gon-sign.hcl"
# artifacts: archive
# ids:
# - main
# signature: "${artifact}.zip"
# output: true
# env:
# - APPLE_DEVELOPER_ID_APPLICATION={{ .Env.APPLE_DEVELOPER_ID }}
# - APPLE_DEVELOPER_TEAM_ID={{ .Env.APPLE_TEAM_ID }}
# - AC_PASSWORD={{ .Env.AC_PASSWORD }}
sboms:
- artifacts: archive
================================================
FILE: .markdownlint.json
================================================
{
"default": true,
"MD013": false,
"MD024": false,
"MD026": false,
"MD031": false,
"MD032": false,
"MD033": {
"allowed_elements": ["br", "kbd", "sub", "sup"]
},
"MD041": false
}
================================================
FILE: .pre-commit-config.yaml
================================================
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
exclude_types: [markdown]
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/tekwizely/pre-commit-golang
rev: v1.0.0-beta.5
hooks:
- id: go-imports-repo
args:
- "-local"
- "github.com/benbjohnson/litestream"
- "-w"
- id: go-vet-repo-mod
- id: go-staticcheck-repo-mod
================================================
FILE: AGENTS.md
================================================
# AGENTS.md - Litestream AI Agent Guide
Litestream is a disaster recovery tool for SQLite that runs as a background process, monitors the WAL, converts changes to immutable LTX files, and replicates them to cloud storage. It uses `modernc.org/sqlite` (pure Go, no CGO required).
## Before You Start
1. Read [AI_PR_GUIDE.md](AI_PR_GUIDE.md) for contribution requirements
2. Check [CONTRIBUTING.md](CONTRIBUTING.md) for what we accept (bug fixes welcome, features need discussion)
3. Review recent PRs for current patterns
## Critical Rules
- **Lock page at 1GB**: SQLite reserves page at 0x40000000. Always skip it. See [docs/SQLITE_INTERNALS.md](docs/SQLITE_INTERNALS.md)
- **LTX files are immutable**: Never modify after creation. See [docs/LTX_FORMAT.md](docs/LTX_FORMAT.md)
- **Single replica per database**: Each DB replicates to exactly one destination
- **Use `litestream ltx`**: Not `litestream wal` (deprecated)
- **Use `litestream reset`**: Clears corrupted local LTX state for a database. See `cmd/litestream/reset.go`
- **`auto-recover` config**: Replica option that automatically resets local state on LTX errors. Disabled by default. See `replica.go`
- **Retention enabled by default**: `Store.RetentionEnabled` is `true` by default. Disable only when cloud lifecycle policies handle cleanup. See `store.go`
- **IPC socket disabled by default**: Control socket is off by default. Enable with `socket.enabled: true` in config. See `server.go`
- **`$PID` config expansion**: Config files support `$PID` to expand to the current process ID, plus standard `$ENV_VAR` expansion. See `cmd/litestream/main.go`
- **`litestream ltx -level`**: Use `-level 0`–`9` or `-level all` to inspect specific compaction levels. See `cmd/litestream/ltx.go`
- **Return errors, don't log them**: Always return errors to callers. Never `log.Printf(err)` and continue — this silently hides failures in a disaster recovery tool. Only use DEBUG log for best-effort operations where failure doesn't affect correctness and a valid fallback exists (e.g., reading SHM mxFrame optimization hint). See [docs/PATTERNS.md](docs/PATTERNS.md#error-handling)
## Layer Boundaries
| Layer | File | Responsibility |
|-------|------|----------------|
| DB | `db.go` | Database state, restoration, WAL monitoring, library API (`SyncStatus`, `SyncAndWait`, `EnsureExists`) |
| Replica | `replica.go` | Replication mechanics only |
| Storage | `**/replica_client.go` | Backend implementations (includes `ReplicaClientV3` for v0.3.x restore) |
| IPC | `server.go` | Unix socket control API (register/unregister, /txid, pprof) |
| Leasing | `leaser.go`, `s3/leaser.go` | Distributed lease acquisition via conditional writes |
Database state logic belongs in DB layer, not Replica layer.
## Quick Reference
**Build:**
```bash
go build -o bin/litestream ./cmd/litestream
go test -race -v ./...
```
**Code quality:**
```bash
pre-commit run --all-files
```
## Documentation
| Document | When to Read |
|----------|--------------|
| [docs/PATTERNS.md](docs/PATTERNS.md) | Code patterns and anti-patterns |
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Deep component details |
| [docs/SQLITE_INTERNALS.md](docs/SQLITE_INTERNALS.md) | WAL format, lock page |
| [docs/LTX_FORMAT.md](docs/LTX_FORMAT.md) | Replication format |
| [docs/TESTING_GUIDE.md](docs/TESTING_GUIDE.md) | Test strategies |
| [docs/REPLICA_CLIENT_GUIDE.md](docs/REPLICA_CLIENT_GUIDE.md) | Adding storage backends |
| [docs/PROVIDER_COMPATIBILITY.md](docs/PROVIDER_COMPATIBILITY.md) | Provider-specific S3/cloud configs |
## Checklist
Before submitting changes:
- [ ] Read relevant docs above
- [ ] Follow patterns in [docs/PATTERNS.md](docs/PATTERNS.md)
- [ ] Test with race detector (`go test -race`)
- [ ] Run `pre-commit run --all-files`
- [ ] For page iteration: test with >1GB databases
- [ ] Show investigation evidence in PR (see [AI_PR_GUIDE.md](AI_PR_GUIDE.md))
================================================
FILE: AI_PR_GUIDE.md
================================================
# AI-Assisted Contribution Guide
This guide helps AI assistants (and humans using them) submit high-quality PRs to Litestream.
## TL;DR Checklist
Before submitting a PR:
- [ ] **Show your investigation** - Include logs, file patterns, or debug output proving the problem
- [ ] **Define scope clearly** - State what this PR does AND does not do
- [ ] **Include runnable test commands** - Not just descriptions, actual `go test` commands
- [ ] **Reference related issues/PRs** - Show awareness of related work
- [ ] **Error handling**: Does the code return errors to callers? Watch for `log.Printf(err)` followed by `continue` or `return nil` — this silently swallows failures.
## What Makes PRs Succeed
Analysis of recent PRs shows successful submissions share these patterns:
### 1. Investigation Artifacts
Show evidence, don't just describe the fix.
**Good:**
```markdown
## Problem
File patterns show excessive snapshot creation after checkpoint:
- 21:43 5.2G snapshot.ltx
- 21:47 5.2G snapshot.ltx (after checkpoint - should not trigger new snapshot)
Debug logs show `verify()` incorrectly detecting position mismatch...
```
**Bad:**
```markdown
## Problem
Snapshots are created too often. This PR fixes it.
```
### 2. Clear Scope Definition
Explicitly state boundaries.
**Good:**
```markdown
## Scope
This PR adds the lease client interface only.
**In scope:**
- LeaseClient interface definition
- Mock implementation for testing
**Not in scope (future PRs):**
- Integration with Store
- Distributed coordination logic
```
**Bad:**
```markdown
## Changes
Added leasing support and also fixed a checkpoint bug I noticed.
```
### 3. Runnable Test Commands
**Good:** Include actual commands that can be run:
```bash
# Unit tests
go test -race -v -run TestDB_CheckpointDoesNotTriggerSnapshot ./...
# Integration test with file backend
go test -v ./replica_client_test.go -integration file
```
**Bad:** Vague descriptions like "Manual testing with file backend" or "Verified it works"
### 4. Before/After Comparison
For behavior changes, show the difference:
**Good:**
```markdown
## Behavior Change
| Scenario | Before | After |
|----------|--------|-------|
| Checkpoint with no changes | Creates snapshot | No snapshot |
| Checkpoint with changes | Creates snapshot | Creates snapshot |
```
## Common Mistakes
### Scope Creep
**Problem:** Mixing unrelated changes in one PR.
**Example:** PR titled "Add lease client" also includes a fix for checkpoint timing.
**Fix:** Split into separate PRs. Reference them: "This PR adds the lease client. The checkpoint fix is in #XXX."
### Missing Root Cause Analysis
**Problem:** Implementing a fix without proving the problem exists.
**Example:** "Add exponential backoff" without showing what's filling disk.
**Fix:** Include investigation showing the actual cause before proposing solution.
### Vague Test Plans
**Problem:** "Tested manually" or "Verified it works."
**Fix:** Include exact commands:
```bash
go test -race -v -run TestSpecificFunction ./...
```
### No Integration Context
**Problem:** Large features without explaining how they fit.
**Fix:** For multi-PR work, explain the phases:
```markdown
This is Phase 1 of 3 for distributed leasing:
1. **This PR**: Lease client interface
2. Future: Store integration
3. Future: Distributed coordination
```
## PR Description Template
Use this structure for PR descriptions:
```text
## Summary
[1-2 sentences: what this PR does]
## Problem
[Evidence of the problem - logs, file patterns, user reports]
## Solution
[Brief explanation of the approach]
## Scope
**In scope:**
- [item]
**Not in scope:**
- [item]
## Test Plan
[Include actual go test commands here]
## Related
- Fixes #XXX
- Related to #YYY
```
## What We Accept
From [CONTRIBUTING.md](CONTRIBUTING.md):
- **Bug fixes** - Welcome, especially with evidence
- **Small improvements** - Performance, code cleanup
- **Documentation** - Always welcome
- **Features** - Discuss in issue first; large features typically implemented internally
## Resources
- [AGENTS.md](AGENTS.md) - Project overview and checklist
- [docs/PATTERNS.md](docs/PATTERNS.md) - Code patterns
- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Litestream
Thank you for your interest in contributing to Litestream! We value community contributions and appreciate your help in making Litestream better.
## Types of Contributions We Accept
### ✅ We Encourage and Accept
- **Bug fixes and patches**: If you've found a bug and have a fix, we welcome your contribution
- **Security vulnerability reports**: Please report security issues responsibly (see Security section below)
- **Documentation improvements**: Help make our docs clearer and more comprehensive
- **Testing and feedback**: Report issues, test new features, and provide feedback
- **Small code improvements**: Performance optimizations, code cleanup, and minor enhancements
### ⚠️ Discuss First
- **Feature requests**: Please open an issue to discuss new features before implementing them
- **Large changes**: For significant modifications, please discuss your approach in an issue first
### ❌ Generally Not Accepted
- **Large external feature contributions**: Features carry a long-term maintenance burden. To reduce burnout and maintain code quality, we typically implement major features internally. This allows us to ensure consistency with the overall architecture and maintain the high reliability that Litestream users depend on for disaster recovery
- **Breaking changes**: Changes that break backward compatibility require extensive discussion
## AI-Assisted Contributions
We welcome AI-assisted contributions for bug fixes and small improvements. Whether you're using Claude, Copilot, Cursor, or other AI tools:
**Requirements:**
- **Show your investigation** - Include evidence (logs, file patterns, debug output) proving the problem exists
- **Define scope clearly** - State what the PR does and does not do
- **Include runnable test commands** - Actual `go test` commands, not just descriptions
- **Human review before submission** - You're responsible for the code you submit
**Resources:**
- [AI_PR_GUIDE.md](AI_PR_GUIDE.md) - Detailed guide with templates and examples
- [AGENTS.md](AGENTS.md) - Project overview for AI assistants
## How to Contribute
### Reporting Bugs
Before reporting a bug:
1. Check the [existing issues](https://github.com/benbjohnson/litestream/issues) to avoid duplicates
2. Verify you're using the latest version of Litestream
3. Gather diagnostic information (OS, version, configuration, error messages)
When reporting a bug, please use our issue template and include:
- Your operating system and version
- Litestream version (`litestream version`)
- Relevant configuration (sanitized of sensitive data)
- Steps to reproduce the issue
- Expected vs actual behavior
- Any error messages or logs
### Submitting Pull Requests
1. **Fork the repository** and create a new branch from `main`
2. **Make your changes** following our code style (see Development section)
3. **Add or update tests** as appropriate
4. **Update documentation** if you're changing behavior
5. **Run tests and linters** locally:
```bash
go test -v ./...
go vet ./...
go fmt ./...
goimports -local github.com/benbjohnson/litestream -w .
pre-commit run --all-files
```
6. **Submit a pull request** with a clear description of your changes
### Pull Request Guidelines
Your PR should:
- Have a clear, descriptive title
- Reference any related issues (e.g., "Fixes #123")
- Include tests for bug fixes and new features
- Pass all CI checks
- Have a focused scope (one bug fix or feature per PR)
## Development Setup
### Prerequisites
- Go 1.24 or later
- CGO enabled (for SQLite integration)
- Git
- Pre-commit (optional but recommended): `pip install pre-commit`
### Building from Source
```bash
# Clone the repository
git clone https://github.com/benbjohnson/litestream.git
cd litestream
# Build the binary
go build ./cmd/litestream
# Run tests
go test -v ./...
# Install pre-commit hooks (recommended)
pre-commit install
```
### Code Style
- Follow standard Go conventions
- Use `gofmt` and `goimports` for formatting
- Run `go vet` and `staticcheck` for static analysis
- Keep functions focused and well-documented
- Add comments for exported types and functions
### Testing
- Write unit tests for new functionality
- Ensure existing tests pass before submitting PRs
- Integration tests require specific environment setup (see test files for details)
## Security
If you discover a security vulnerability, please:
1. **DO NOT** open a public issue
2. Email the maintainers directly with details
3. Allow time for the issue to be addressed before public disclosure
## Code of Conduct
We expect all contributors to:
- Be respectful and inclusive
- Welcome newcomers and help them get started
- Focus on constructive criticism
- Respect differing viewpoints and experiences
## Getting Help
- **Documentation**: [litestream.io](https://litestream.io)
- **Issues**: [GitHub Issues](https://github.com/benbjohnson/litestream/issues)
## License
By contributing to Litestream, you agree that your contributions will be licensed under the Apache License 2.0, the same as the project.
## Acknowledgments
Thank you to all our contributors! Your efforts help make Litestream a reliable disaster recovery tool for the SQLite community.
================================================
FILE: Dockerfile
================================================
FROM golang:1.25 AS builder
# Install build dependencies for VFS extension
RUN apt-get update && apt-get install -y gcc libc6-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /src/litestream
COPY . .
ARG LITESTREAM_VERSION=latest
# Build litestream binary
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
go build -ldflags "-s -w -X 'main.Version=${LITESTREAM_VERSION}' -extldflags '-static'" -tags osusergo,netgo,sqlite_omit_load_extension -o /usr/local/bin/litestream ./cmd/litestream
# Build VFS loadable extension
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
mkdir -p dist && \
CGO_ENABLED=1 go build \
-tags "vfs,SQLITE3VFS_LOADABLE_EXT" \
-buildmode=c-archive \
-o dist/litestream-vfs.a ./cmd/litestream-vfs && \
mv dist/litestream-vfs.h src/litestream-vfs.h && \
gcc -DSQLITE3VFS_LOADABLE_EXT -g -fPIC -shared \
-o dist/litestream-vfs.so \
src/litestream-vfs.c \
dist/litestream-vfs.a \
-lpthread -ldl -lm
# --- Hardened image (Scratch) ---
FROM alpine:3.21 AS certs
RUN apk --update add ca-certificates && \
echo "nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin" > /etc/minimal-passwd && \
echo "nonroot:x:65532:" > /etc/minimal-group
FROM scratch AS hardened
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=certs /etc/minimal-passwd /etc/passwd
COPY --from=certs /etc/minimal-group /etc/group
COPY --from=builder /usr/local/bin/litestream /usr/local/bin/litestream
USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/litestream"]
CMD []
# --- Default image (Debian) ---
FROM debian:bookworm-slim AS default
RUN apt-get update && \
apt-get install -y ca-certificates sqlite3 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/bin/litestream /usr/local/bin/litestream
COPY --from=builder /src/litestream/dist/litestream-vfs.so /usr/local/lib/litestream-vfs.so
ENTRYPOINT ["/usr/local/bin/litestream"]
CMD []
================================================
FILE: GEMINI.md
================================================
# GEMINI.md - Gemini Code Assist Configuration
Gemini-specific configuration for Litestream. See [AGENTS.md](AGENTS.md) for project documentation.
## Before Contributing
1. Read [AI_PR_GUIDE.md](AI_PR_GUIDE.md) - PR quality requirements
2. Read [AGENTS.md](AGENTS.md) - Project overview and checklist
3. Check [CONTRIBUTING.md](CONTRIBUTING.md) - What we accept
## File Exclusions
Check `.aiexclude` for patterns of files that should not be shared with Gemini.
## Gemini Strengths for This Project
- **Test generation** - Creating comprehensive test suites
- **Documentation** - Generating and updating docs
- **Code review** - Identifying issues and security concerns
- **Local codebase awareness** - Enable for full repository understanding
## Documentation
Load as needed:
- [docs/PATTERNS.md](docs/PATTERNS.md) - Code patterns when writing code
- [docs/SQLITE_INTERNALS.md](docs/SQLITE_INTERNALS.md) - For WAL/page work
- [docs/TESTING_GUIDE.md](docs/TESTING_GUIDE.md) - For test generation
## Critical Rules
- **Lock page at 1GB** - Skip page at 0x40000000
- **LTX files are immutable** - Never modify after creation
- **Layer boundaries** - DB handles state, Replica handles replication
## Quick Commands
```bash
go build -o bin/litestream ./cmd/litestream
go test -race -v ./...
pre-commit run --all-files
```
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See th
gitextract_ajbk2a5_/
├── .aiexclude
├── .claude/
│ ├── agents/
│ │ ├── ltx-compaction-specialist.md
│ │ ├── performance-optimizer.md
│ │ ├── replica-client-developer.md
│ │ ├── sqlite-expert.md
│ │ └── test-engineer.md
│ ├── commands/
│ │ ├── add-storage-backend.md
│ │ ├── analyze-ltx.md
│ │ ├── debug-ipc.md
│ │ ├── debug-wal.md
│ │ ├── fix-common-issues.md
│ │ ├── run-comprehensive-tests.md
│ │ ├── test-compaction.md
│ │ ├── trace-replication.md
│ │ └── validate-replica.md
│ └── settings.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── commit.yml
│ ├── integration-tests.yml
│ ├── manual-integration-tests.yml
│ ├── pr-metrics.yml
│ ├── pre-release-checklist.yml
│ ├── release.docker.yml
│ ├── release.yml
│ ├── stale-issues.yml
│ └── upgrade-tests.yml
├── .gitignore
├── .goreleaser.yml
├── .markdownlint.json
├── .pre-commit-config.yaml
├── AGENTS.md
├── AI_PR_GUIDE.md
├── CONTRIBUTING.md
├── Dockerfile
├── GEMINI.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── _examples/
│ └── library/
│ ├── README.md
│ ├── basic/
│ │ └── main.go
│ ├── library_example_test.go
│ └── s3/
│ └── main.go
├── abs/
│ └── replica_client.go
├── cmd/
│ ├── litestream/
│ │ ├── databases.go
│ │ ├── directory_watcher.go
│ │ ├── directory_watcher_stress_test.go
│ │ ├── directory_watcher_test.go
│ │ ├── info.go
│ │ ├── info_test.go
│ │ ├── list.go
│ │ ├── list_test.go
│ │ ├── ltx.go
│ │ ├── ltx_test.go
│ │ ├── main.go
│ │ ├── main_notwindows.go
│ │ ├── main_test.go
│ │ ├── main_windows.go
│ │ ├── mcp.go
│ │ ├── register.go
│ │ ├── register_test.go
│ │ ├── replicate.go
│ │ ├── replicate_test.go
│ │ ├── reset.go
│ │ ├── restore.go
│ │ ├── restore_test.go
│ │ ├── start.go
│ │ ├── status.go
│ │ ├── status_test.go
│ │ ├── stop.go
│ │ ├── sync.go
│ │ ├── sync_test.go
│ │ ├── unregister.go
│ │ ├── unregister_test.go
│ │ └── version.go
│ ├── litestream-test/
│ │ ├── README.md
│ │ ├── S3-RETENTION-TESTING.md
│ │ ├── load.go
│ │ ├── main.go
│ │ ├── populate.go
│ │ ├── scripts/
│ │ │ ├── README.md
│ │ │ ├── reproduce-critical-bug.sh
│ │ │ ├── test-754-restore-focus.sh
│ │ │ ├── test-754-s3-scenarios.sh
│ │ │ ├── test-format-isolation.sh
│ │ │ ├── test-massive-upgrade.sh
│ │ │ ├── test-quick-format-check.sh
│ │ │ ├── test-s3-access-point.sh
│ │ │ ├── test-s3-retention-cleanup.sh
│ │ │ ├── test-s3-retention-comprehensive.sh
│ │ │ ├── test-s3-retention-large-db.sh
│ │ │ ├── test-s3-retention-small-db.sh
│ │ │ ├── test-simple-754-reproduction.sh
│ │ │ ├── test-upgrade-large-db.sh
│ │ │ ├── test-upgrade-v0.3-to-v0.5.sh
│ │ │ ├── test-v0.5-flag-reproduction.sh
│ │ │ ├── test-v0.5-restart-scenarios.sh
│ │ │ └── verify-test-setup.sh
│ │ ├── shrink.go
│ │ └── validate.go
│ └── litestream-vfs/
│ ├── chaos_test.go
│ ├── fuzz_test.go
│ ├── hydration_e2e_test.go
│ ├── main.go
│ ├── main_test.go
│ ├── stress_test.go
│ ├── time_travel_test.go
│ ├── vfs_soak_test.go
│ └── vfs_write_integration_test.go
├── compaction_level.go
├── compactor.go
├── compactor_test.go
├── db.go
├── db_internal_test.go
├── db_shutdown_test.go
├── db_test.go
├── docker-compose.test.yml
├── docs/
│ ├── ARCHITECTURE.md
│ ├── DOC_MAINTENANCE.md
│ ├── LTX_FORMAT.md
│ ├── PATTERNS.md
│ ├── PENDING_USER_DOCS.md
│ ├── PROVIDER_COMPATIBILITY.md
│ ├── REPLICA_CLIENT_GUIDE.md
│ ├── SQLITE_INTERNALS.md
│ ├── TESTING_GUIDE.md
│ └── VFS.md
├── etc/
│ ├── build.ps1
│ ├── gon-sign.hcl
│ ├── gon.hcl
│ ├── litestream.service
│ ├── litestream.wxs
│ ├── litestream.yml
│ ├── nfpm.yml
│ ├── run-s3-docker-tests.sh
│ └── s3_mock.py
├── file/
│ ├── replica_client.go
│ └── replica_client_test.go
├── go.mod
├── go.sum
├── grafana/
│ ├── README.md
│ └── litestream-dashboard.json
├── gs/
│ ├── replica_client.go
│ └── replica_client_test.go
├── heartbeat.go
├── heartbeat_test.go
├── internal/
│ ├── hexdump.go
│ ├── internal.go
│ ├── internal_unix.go
│ ├── internal_windows.go
│ ├── limit_read_closer.go
│ ├── lock_unix.go
│ ├── lock_windows.go
│ ├── resumable_reader.go
│ ├── resumable_reader_test.go
│ └── testingutil/
│ └── testingutil.go
├── leaser.go
├── litestream.go
├── litestream_test.go
├── llms.txt
├── log.go
├── mock/
│ └── replica_client.go
├── nats/
│ ├── replica_client.go
│ └── replica_client_test.go
├── oss/
│ ├── replica_client.go
│ └── replica_client_test.go
├── packages/
│ ├── npm/
│ │ ├── litestream-vfs/
│ │ │ ├── README.md
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── litestream-vfs-darwin-amd64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── litestream-vfs-darwin-arm64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── litestream-vfs-linux-amd64/
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ └── litestream-vfs-linux-arm64/
│ │ ├── README.md
│ │ └── package.json
│ ├── python/
│ │ ├── MANIFEST.in
│ │ ├── README.md
│ │ ├── litestream_vfs/
│ │ │ ├── __init__.py
│ │ │ └── noop.c
│ │ ├── scripts/
│ │ │ └── rename_wheel.py
│ │ └── setup.py
│ └── ruby/
│ ├── README.md
│ ├── lib/
│ │ └── litestream_vfs.rb
│ └── litestream-vfs.gemspec
├── replica.go
├── replica_client.go
├── replica_client_test.go
├── replica_internal_test.go
├── replica_test.go
├── replica_url.go
├── replica_url_test.go
├── restore_fuzz_test.go
├── s3/
│ ├── leaser.go
│ ├── leaser_test.go
│ ├── replica_client.go
│ └── replica_client_test.go
├── scripts/
│ ├── README.md
│ ├── analyze-test-results.sh
│ ├── run-upgrade-tests.sh
│ └── setup-homebrew-tap.sh
├── server.go
├── server_test.go
├── sftp/
│ └── replica_client.go
├── skills/
│ └── litestream/
│ ├── SKILL.md
│ ├── references/
│ │ ├── ARCHITECTURE.md
│ │ ├── LTX_FORMAT.md
│ │ ├── PATTERNS.md
│ │ ├── REPLICA_CLIENT_GUIDE.md
│ │ ├── SQLITE_INTERNALS.md
│ │ └── TESTING_GUIDE.md
│ └── scripts/
│ └── validate-setup.sh
├── src/
│ ├── litestream-vfs.c
│ ├── sqlite3.h
│ ├── sqlite3ext.h
│ └── sqlite3vfs.h
├── store.go
├── store_compaction_remote_test.go
├── store_test.go
├── testdata/
│ ├── db/
│ │ ├── enforce-retention/
│ │ │ └── database
│ │ └── write-snapshot-to/
│ │ └── database
│ └── wal-reader/
│ ├── frame-checksum-mismatch/
│ │ └── wal
│ ├── frame-salts/
│ │ └── wal
│ ├── ok/
│ │ └── wal
│ └── salt-mismatch/
│ └── wal
├── tests/
│ ├── cpu-usage/
│ │ ├── README.md
│ │ ├── litestream-test-polling.yml
│ │ └── test-cpu-usage.sh
│ └── integration/
│ ├── README.md
│ ├── boundary_test.go
│ ├── compatibility_test.go
│ ├── comprehensive_soak_test.go
│ ├── concurrent_test.go
│ ├── directory_watcher_helpers.go
│ ├── directory_watcher_test.go
│ ├── docker_helpers.go
│ ├── fixtures.go
│ ├── helpers.go
│ ├── minio_soak_test.go
│ ├── overnight_s3_soak_test.go
│ ├── overnight_test.go
│ ├── profile_test.go
│ ├── quick_test.go
│ ├── s3_access_point_test.go
│ ├── s3_restore_connection_drop_test.go
│ ├── scenario_test.go
│ ├── shutdown_retry_test.go
│ ├── soak_helpers.go
│ ├── supabase-s3/
│ │ ├── docker-compose.yml
│ │ └── test.sh
│ └── upgrade_test.go
├── v3.go
├── v3_test.go
├── vfs.go
├── vfs_compaction_test.go
├── vfs_test.go
├── vfs_write_test.go
├── wal_reader.go
├── wal_reader_test.go
└── webdav/
├── replica_client.go
└── replica_client_test.go
SYMBOL INDEX (1908 symbols across 138 files)
FILE: _examples/library/basic/main.go
function main (line 25) | func main() {
function run (line 31) | func run(ctx context.Context) error {
function openAppDB (line 101) | func openAppDB(_ context.Context, path string) (*sql.DB, error) {
function initSchema (line 106) | func initSchema(ctx context.Context, db *sql.DB) error {
function insertRow (line 117) | func insertRow(ctx context.Context, db *sql.DB) error {
FILE: _examples/library/library_example_test.go
function TestLibraryExampleFileBackend (line 18) | func TestLibraryExampleFileBackend(t *testing.T) {
function openAppDB (line 92) | func openAppDB(ctx context.Context, path string) (*sql.DB, error) {
function waitForLTXFiles (line 108) | func waitForLTXFiles(replicaPath string, timeout time.Duration) error {
FILE: _examples/library/s3/main.go
constant dbPath (line 36) | dbPath = "./myapp.db"
function main (line 38) | func main() {
function run (line 44) | func run(ctx context.Context) error {
function restoreIfNotExists (line 134) | func restoreIfNotExists(ctx context.Context, client *s3.ReplicaClient, d...
function openAppDB (line 171) | func openAppDB(_ context.Context, path string) (*sql.DB, error) {
function initSchema (line 176) | func initSchema(ctx context.Context, db *sql.DB) error {
function insertRow (line 187) | func insertRow(ctx context.Context, db *sql.DB) error {
FILE: abs/replica_client.go
function init (line 32) | func init() {
constant ReplicaClientType (line 37) | ReplicaClientType = "abs"
constant MetadataKeyTimestamp (line 41) | MetadataKeyTimestamp = "litestreamtimestamp"
type ReplicaClient (line 46) | type ReplicaClient struct
method SetLogger (line 69) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 95) | func (c *ReplicaClient) Type() string {
method Init (line 100) | func (c *ReplicaClient) Init(ctx context.Context) (err error) {
method LTXFiles (line 209) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method WriteLTXFile (line 217) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method OpenLTXFile (line 267) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method DeleteLTXFiles (line 293) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method DeleteAll (line 317) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
function NewReplicaClient (line 63) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 76) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
type ltxFileIterator (line 356) | type ltxFileIterator struct
method Close (line 398) | func (itr *ltxFileIterator) Close() (err error) {
method Next (line 404) | func (itr *ltxFileIterator) Next() bool {
method loadNextPage (line 428) | func (itr *ltxFileIterator) loadNextPage() bool {
method Err (line 487) | func (itr *ltxFileIterator) Err() error { return itr.err }
method Item (line 489) | func (itr *ltxFileIterator) Item() *ltx.FileInfo {
function newLTXFileIterator (line 372) | func newLTXFileIterator(ctx context.Context, client *ReplicaClient, leve...
function isNotExists (line 493) | func isNotExists(err error) bool {
FILE: cmd/litestream-test/load.go
type LoadCommand (line 21) | type LoadCommand struct
method Run (line 42) | func (c *LoadCommand) Run(ctx context.Context, args []string) error {
method generateLoad (line 77) | func (c *LoadCommand) generateLoad(ctx context.Context) error {
method worker (line 124) | func (c *LoadCommand) worker(ctx context.Context, db *sql.DB, workerID...
method calculateRate (line 160) | func (c *LoadCommand) calculateRate(stats *LoadStats) float64 {
method ensureTestTable (line 193) | func (c *LoadCommand) ensureTestTable(db *sql.DB) error {
method performWrite (line 212) | func (c *LoadCommand) performWrite(db *sql.DB, data []byte) error {
method performRead (line 225) | func (c *LoadCommand) performRead(db *sql.DB) error {
method reportStats (line 233) | func (c *LoadCommand) reportStats(ctx context.Context, stats *LoadStat...
method finalReport (line 268) | func (c *LoadCommand) finalReport(stats *LoadStats) {
method Usage (line 284) | func (c *LoadCommand) Usage() {
type LoadStats (line 33) | type LoadStats struct
function waveFunction (line 178) | func waveFunction(t float64) float64 {
function sinApprox (line 182) | func sinApprox(x float64) float64 {
FILE: cmd/litestream-test/main.go
function main (line 19) | func main() {
type Main (line 27) | type Main struct
method Run (line 41) | func (m *Main) Run(ctx context.Context, args []string) error {
method Usage (line 69) | func (m *Main) Usage() {
function NewMain (line 33) | func NewMain() *Main {
type VersionCommand (line 89) | type VersionCommand struct
method Run (line 93) | func (c *VersionCommand) Run(ctx context.Context, args []string) error {
method Usage (line 108) | func (c *VersionCommand) Usage() {
function init (line 118) | func init() {
FILE: cmd/litestream-test/populate.go
type PopulateCommand (line 17) | type PopulateCommand struct
method Run (line 29) | func (c *PopulateCommand) Run(ctx context.Context, args []string) error {
method populateDatabase (line 69) | func (c *PopulateCommand) populateDatabase(ctx context.Context, target...
method populateTable (line 171) | func (c *PopulateCommand) populateTable(ctx context.Context, db *sql.D...
method Usage (line 223) | func (c *PopulateCommand) Usage() {
function parseSize (line 273) | func parseSize(s string) (int64, error) {
function getDatabaseSize (line 303) | func getDatabaseSize(path string) (int64, error) {
FILE: cmd/litestream-test/shrink.go
type ShrinkCommand (line 15) | type ShrinkCommand struct
method Run (line 25) | func (c *ShrinkCommand) Run(ctx context.Context, args []string) error {
method shrinkDatabase (line 59) | func (c *ShrinkCommand) shrinkDatabase(ctx context.Context) error {
method getTableList (line 147) | func (c *ShrinkCommand) getTableList(db *sql.DB) ([]string, error) {
method deleteFromTable (line 171) | func (c *ShrinkCommand) deleteFromTable(db *sql.DB, table string) (int...
method runCheckpoint (line 248) | func (c *ShrinkCommand) runCheckpoint(db *sql.DB) error {
method runVacuum (line 272) | func (c *ShrinkCommand) runVacuum(db *sql.DB) error {
method Usage (line 287) | func (c *ShrinkCommand) Usage() {
FILE: cmd/litestream-test/validate.go
type ValidateCommand (line 19) | type ValidateCommand struct
method Run (line 38) | func (c *ValidateCommand) Run(ctx context.Context, args []string) error {
method performRestore (line 99) | func (c *ValidateCommand) performRestore(ctx context.Context) Validati...
method performQuickCheck (line 154) | func (c *ValidateCommand) performQuickCheck(ctx context.Context) Valid...
method performIntegrityCheck (line 194) | func (c *ValidateCommand) performIntegrityCheck(ctx context.Context) V...
method performChecksumCheck (line 249) | func (c *ValidateCommand) performChecksumCheck(ctx context.Context) Va...
method performDataValidation (line 292) | func (c *ValidateCommand) performDataValidation(ctx context.Context) V...
method validateLTXContinuity (line 365) | func (c *ValidateCommand) validateLTXContinuity(ctx context.Context) V...
method calculateDBChecksum (line 401) | func (c *ValidateCommand) calculateDBChecksum(path string) ([]byte, er...
method getTableList (line 416) | func (c *ValidateCommand) getTableList(db *sql.DB) ([]string, error) {
method getRowCount (line 435) | func (c *ValidateCommand) getRowCount(db *sql.DB, table string) (int, ...
method reportResults (line 442) | func (c *ValidateCommand) reportResults(results []ValidationResult) er...
method Usage (line 462) | func (c *ValidateCommand) Usage() {
type ValidationResult (line 30) | type ValidationResult struct
FILE: cmd/litestream-vfs/chaos_test.go
function TestVFS_ChaosEngineering (line 22) | func TestVFS_ChaosEngineering(t *testing.T) {
function newChaosReplicaClient (line 174) | func newChaosReplicaClient(base litestream.ReplicaClient) *chaosReplicaC...
type chaosReplicaClient (line 181) | type chaosReplicaClient struct
method LTXFiles (line 188) | func (c *chaosReplicaClient) LTXFiles(ctx context.Context, level int, ...
method OpenLTXFile (line 199) | func (c *chaosReplicaClient) OpenLTXFile(ctx context.Context, level in...
FILE: cmd/litestream-vfs/fuzz_test.go
function TestVFS_FuzzSeedCorpus (line 23) | func TestVFS_FuzzSeedCorpus(t *testing.T) {
function FuzzVFSReplicaReadPatterns (line 38) | func FuzzVFSReplicaReadPatterns(f *testing.F) {
function runVFSFuzzWorkload (line 48) | func runVFSFuzzWorkload(tb testing.TB, corpus []byte) {
FILE: cmd/litestream-vfs/hydration_e2e_test.go
function TestHydration_E2E_SQLiteCLI (line 27) | func TestHydration_E2E_SQLiteCLI(t *testing.T) {
function TestHydration_E2E_SQLiteCLI_TempFile (line 68) | func TestHydration_E2E_SQLiteCLI_TempFile(t *testing.T) {
function TestHydration_E2E_SQLiteCLI_Disabled (line 95) | func TestHydration_E2E_SQLiteCLI_Disabled(t *testing.T) {
function TestHydration_E2E_SQLiteCLI_MultipleQueries (line 128) | func TestHydration_E2E_SQLiteCLI_MultipleQueries(t *testing.T) {
function buildVFSExtension (line 171) | func buildVFSExtension(t *testing.T) string {
function findProjectRoot (line 228) | func findProjectRoot(t *testing.T) string {
function runSQLiteCLI (line 248) | func runSQLiteCLI(t *testing.T, extPath string, env []string, query stri...
function setupTestReplica (line 279) | func setupTestReplica(t *testing.T, client litestream.ReplicaClient) {
function setupTestReplicaWithMoreData (line 305) | func setupTestReplicaWithMoreData(t *testing.T, client litestream.Replic...
FILE: cmd/litestream-vfs/main.go
function main (line 39) | func main() {}
function LitestreamVFSRegister (line 42) | func LitestreamVFSRegister() *C.char {
function GoLitestreamRegisterConnection (line 115) | func GoLitestreamRegisterConnection(dbPtr unsafe.Pointer, fileID C.sqlit...
function GoLitestreamUnregisterConnection (line 123) | func GoLitestreamUnregisterConnection(dbPtr unsafe.Pointer) *C.char {
function GoLitestreamSetTime (line 129) | func GoLitestreamSetTime(dbPtr unsafe.Pointer, timestamp *C.char) *C.char {
function GoLitestreamResetTime (line 140) | func GoLitestreamResetTime(dbPtr unsafe.Pointer) *C.char {
function GoLitestreamTime (line 148) | func GoLitestreamTime(dbPtr unsafe.Pointer, out **C.char) *C.char {
function GoLitestreamTxid (line 160) | func GoLitestreamTxid(dbPtr unsafe.Pointer, out **C.char) *C.char {
function GoLitestreamLag (line 172) | func GoLitestreamLag(dbPtr unsafe.Pointer, out *C.sqlite3_int64) *C.char {
FILE: cmd/litestream-vfs/main_test.go
function TestVFS_Simple (line 34) | func TestVFS_Simple(t *testing.T) {
function TestVFS_Updating (line 69) | func TestVFS_Updating(t *testing.T) {
function TestVFS_ActiveReadTransaction (line 121) | func TestVFS_ActiveReadTransaction(t *testing.T) {
function TestVFS_PollsL1Files (line 245) | func TestVFS_PollsL1Files(t *testing.T) {
function TestVFS_LongRunningTxnStress (line 399) | func TestVFS_LongRunningTxnStress(t *testing.T) {
function TestVFS_HighLoadConcurrentReads (line 487) | func TestVFS_HighLoadConcurrentReads(t *testing.T) {
function TestVFS_OverlappingTransactionCommitStorm (line 618) | func TestVFS_OverlappingTransactionCommitStorm(t *testing.T) {
function TestVFS_CacheMissStorm (line 750) | func TestVFS_CacheMissStorm(t *testing.T) {
function BenchmarkVFS_LargeDatabase (line 791) | func BenchmarkVFS_LargeDatabase(b *testing.B) {
function TestVFS_NetworkLatencySensitivity (line 825) | func TestVFS_NetworkLatencySensitivity(t *testing.T) {
function TestVFS_ConcurrentConnectionScaling (line 853) | func TestVFS_ConcurrentConnectionScaling(t *testing.T) {
function TestVFS_PRAGMAQueryBehavior (line 921) | func TestVFS_PRAGMAQueryBehavior(t *testing.T) {
function TestVFS_SortingLargeResultSet (line 987) | func TestVFS_SortingLargeResultSet(t *testing.T) {
function TestVFS_ConcurrentIndexAccessRaces (line 1033) | func TestVFS_ConcurrentIndexAccessRaces(t *testing.T) {
function TestVFS_MultiplePageSizes (line 1166) | func TestVFS_MultiplePageSizes(t *testing.T) {
function TestVFS_WaitsForInitialSnapshot (line 1253) | func TestVFS_WaitsForInitialSnapshot(t *testing.T) {
function TestVFS_StorageFailureInjection (line 1309) | func TestVFS_StorageFailureInjection(t *testing.T) {
function TestVFS_PartialLTXUpload (line 1396) | func TestVFS_PartialLTXUpload(t *testing.T) {
function TestVFS_S3EventualConsistency (line 1450) | func TestVFS_S3EventualConsistency(t *testing.T) {
function TestVFS_FileDescriptorBudget (line 1476) | func TestVFS_FileDescriptorBudget(t *testing.T) {
function TestVFS_PageIndexOOM (line 1566) | func TestVFS_PageIndexOOM(t *testing.T) {
function TestVFS_PageIndexCorruptionRecovery (line 1623) | func TestVFS_PageIndexCorruptionRecovery(t *testing.T) {
function TestVFS_RapidUpdateCoalescing (line 1669) | func TestVFS_RapidUpdateCoalescing(t *testing.T) {
function TestVFS_NonContiguousTXIDGapFailsOnOpen (line 1718) | func TestVFS_NonContiguousTXIDGapFailsOnOpen(t *testing.T) {
function TestVFS_PollingThreadRecoversFromLTXListFailure (line 1740) | func TestVFS_PollingThreadRecoversFromLTXListFailure(t *testing.T) {
function TestVFS_PollIntervalEdgeCases (line 1785) | func TestVFS_PollIntervalEdgeCases(t *testing.T) {
function TestVFS_PooledWriteNoFalseConflict (line 1826) | func TestVFS_PooledWriteNoFalseConflict(t *testing.T) {
function TestVFS_PooledWriteStress (line 1890) | func TestVFS_PooledWriteStress(t *testing.T) {
function newVFS (line 1953) | func newVFS(tb testing.TB, client litestream.ReplicaClient) *testVFS {
type testVFS (line 1968) | type testVFS struct
method Open (line 1975) | func (v *testVFS) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite...
method Inject (line 1983) | func (v *testVFS) Inject(path string, err error) {
method popFailure (line 1989) | func (v *testVFS) popFailure(path string) error {
type injectingFile (line 2008) | type injectingFile struct
method ReadAt (line 2015) | func (f *injectingFile) ReadAt(p []byte, off int64) (int, error) {
method FileControl (line 2022) | func (f *injectingFile) FileControl(op int, pragmaName string, pragmaV...
function registerTestVFS (line 2029) | func registerTestVFS(tb testing.TB, vfs sqlite3vfs.VFS) string {
function openReplicatedPrimary (line 2038) | func openReplicatedPrimary(tb testing.TB, client litestream.ReplicaClien...
function forceReplicaSync (line 2053) | func forceReplicaSync(tb testing.TB, db *litestream.DB) {
function openVFSReplicaDB (line 2067) | func openVFSReplicaDB(tb testing.TB, vfsName string) *sql.DB {
function waitForReplicaRowCount (line 2083) | func waitForReplicaRowCount(tb testing.TB, primary, replica *sql.DB, tim...
function waitForTableRowCount (line 2098) | func waitForTableRowCount(tb testing.TB, primary, replica *sql.DB, table...
function fetchOrderedPayloads (line 2114) | func fetchOrderedPayloads(tb testing.TB, db *sql.DB, limit int, orderBy ...
function seedLargeTable (line 2137) | func seedLargeTable(tb testing.TB, db *sql.DB, n int) {
function seedSortedDataset (line 2161) | func seedSortedDataset(tb testing.TB, db *sql.DB, n int) {
function randomPayload (line 2185) | func randomPayload(r *rand.Rand, n int) string {
function pageSizedPayload (line 2194) | func pageSizedPayload(pageSize int, row int) string {
function isBusyError (line 2210) | func isBusyError(err error) bool {
function writeSinglePageLTXFile (line 2231) | func writeSinglePageLTXFile(tb testing.TB, client *file.ReplicaClient, t...
type latencyReplicaClient (line 2263) | type latencyReplicaClient struct
method OpenLTXFile (line 2268) | func (c *latencyReplicaClient) OpenLTXFile(ctx context.Context, level ...
method LTXFiles (line 2273) | func (c *latencyReplicaClient) LTXFiles(ctx context.Context, level int...
type eventualConsistencyClient (line 2278) | type eventualConsistencyClient struct
method LTXFiles (line 2283) | func (c *eventualConsistencyClient) LTXFiles(ctx context.Context, leve...
type observingReplicaClient (line 2290) | type observingReplicaClient struct
method LTXFiles (line 2322) | func (c *observingReplicaClient) LTXFiles(ctx context.Context, level i...
type fdLimitedReplicaClient (line 2295) | type fdLimitedReplicaClient struct
method OpenLTXFile (line 2302) | func (c *fdLimitedReplicaClient) OpenLTXFile(ctx context.Context, leve...
type flakyLTXClient (line 2327) | type flakyLTXClient struct
method LTXFiles (line 2333) | func (c *flakyLTXClient) LTXFiles(ctx context.Context, level int, seek...
type oomPageIndexClient (line 2341) | type oomPageIndexClient struct
method OpenLTXFile (line 2347) | func (c *oomPageIndexClient) OpenLTXFile(ctx context.Context, level in...
type corruptingPageIndexClient (line 2355) | type corruptingPageIndexClient struct
method OpenLTXFile (line 2361) | func (c *corruptingPageIndexClient) OpenLTXFile(ctx context.Context, l...
type hookedReadCloser (line 2381) | type hookedReadCloser struct
method Close (line 2387) | func (h *hookedReadCloser) Close() error {
function waitForLTXFiles (line 2399) | func waitForLTXFiles(t *testing.T, client litestream.ReplicaClient, time...
function waitForReplicaValue (line 2412) | func waitForReplicaValue(t *testing.T, db *sql.DB, query string, expecte...
FILE: cmd/litestream-vfs/stress_test.go
function TestVFS_RaceStressHarness (line 19) | func TestVFS_RaceStressHarness(t *testing.T) {
FILE: cmd/litestream-vfs/time_travel_test.go
function TestVFS_TimeTravelFunctions (line 24) | func TestVFS_TimeTravelFunctions(t *testing.T) {
function TestVFS_PragmaLitestreamTxid (line 122) | func TestVFS_PragmaLitestreamTxid(t *testing.T) {
function TestVFS_PragmaLitestreamLag (line 172) | func TestVFS_PragmaLitestreamLag(t *testing.T) {
function TestVFS_PragmaRelativeTime (line 224) | func TestVFS_PragmaRelativeTime(t *testing.T) {
function fetchLTXCreatedAt (line 324) | func fetchLTXCreatedAt(tb testing.TB, ctx context.Context, client litest...
FILE: cmd/litestream-vfs/vfs_soak_test.go
function TestVFS_LongRunningSoak (line 24) | func TestVFS_LongRunningSoak(t *testing.T) {
FILE: cmd/litestream-vfs/vfs_write_integration_test.go
function TestVFS_WriteAndSync_FileBackend (line 33) | func TestVFS_WriteAndSync_FileBackend(t *testing.T) {
function TestVFS_ReadYourWrites (line 96) | func TestVFS_ReadYourWrites(t *testing.T) {
function TestVFS_MultipleTransactions (line 132) | func TestVFS_MultipleTransactions(t *testing.T) {
function TestVFS_LargeTransaction (line 163) | func TestVFS_LargeTransaction(t *testing.T) {
function TestVFS_PeriodicSync (line 212) | func TestVFS_PeriodicSync(t *testing.T) {
function TestVFS_SyncDuringTransaction (line 242) | func TestVFS_SyncDuringTransaction(t *testing.T) {
function TestVFS_ManualSyncOnly (line 286) | func TestVFS_ManualSyncOnly(t *testing.T) {
function TestVFS_WriteBufferDiscardedOnOpen (line 326) | func TestVFS_WriteBufferDiscardedOnOpen(t *testing.T) {
function TestVFS_WriteBufferDuplicatePages (line 370) | func TestVFS_WriteBufferDuplicatePages(t *testing.T) {
function TestVFS_ExistingBufferDiscarded (line 418) | func TestVFS_ExistingBufferDiscarded(t *testing.T) {
function TestVFS_WriteBufferCorrupted (line 446) | func TestVFS_WriteBufferCorrupted(t *testing.T) {
function TestVFS_ConflictDetection (line 478) | func TestVFS_ConflictDetection(t *testing.T) {
function TestVFS_NoConflictWhenRemoteUnchanged (line 510) | func TestVFS_NoConflictWhenRemoteUnchanged(t *testing.T) {
function TestVFS_ConcurrentReaders (line 543) | func TestVFS_ConcurrentReaders(t *testing.T) {
function TestVFS_ReadWhileWriting (line 585) | func TestVFS_ReadWhileWriting(t *testing.T) {
function TestVFS_Truncate (line 642) | func TestVFS_Truncate(t *testing.T) {
function TestVFS_EmptyTransaction (line 682) | func TestVFS_EmptyTransaction(t *testing.T) {
function TestVFS_SchemaChanges (line 712) | func TestVFS_SchemaChanges(t *testing.T) {
function TestVFS_BlobData (line 758) | func TestVFS_BlobData(t *testing.T) {
function TestVFS_WriteAndRestore (line 798) | func TestVFS_WriteAndRestore(t *testing.T) {
function TestVFS_WriteReadVFSOnly (line 835) | func TestVFS_WriteReadVFSOnly(t *testing.T) {
function TestVFS_MixedWorkload (line 869) | func TestVFS_MixedWorkload(t *testing.T) {
function TestVFS_SyncNetworkError (line 911) | func TestVFS_SyncNetworkError(t *testing.T) {
function TestVFS_InvalidPageSize (line 937) | func TestVFS_InvalidPageSize(t *testing.T) {
function TestVFS_RollbackRestoresOriginalState (line 976) | func TestVFS_RollbackRestoresOriginalState(t *testing.T) {
function TestVFS_RollbackAfterUpdate (line 1037) | func TestVFS_RollbackAfterUpdate(t *testing.T) {
function TestVFS_RollbackAfterDelete (line 1088) | func TestVFS_RollbackAfterDelete(t *testing.T) {
function TestVFS_CommitAfterRollbackWorks (line 1145) | func TestVFS_CommitAfterRollbackWorks(t *testing.T) {
function newWritableVFS (line 1211) | func newWritableVFS(tb testing.TB, client litestream.ReplicaClient, sync...
function newReadOnlyVFS (line 1231) | func newReadOnlyVFS(tb testing.TB, client litestream.ReplicaClient) *lit...
function setupInitialDB (line 1245) | func setupInitialDB(t *testing.T, client litestream.ReplicaClient) {
function countLTXFiles (line 1276) | func countLTXFiles(t *testing.T, client litestream.ReplicaClient) int {
function addExternalLTXFile (line 1291) | func addExternalLTXFile(t *testing.T, client litestream.ReplicaClient, r...
function restoreDB (line 1333) | func restoreDB(t *testing.T, client litestream.ReplicaClient, outputPath...
FILE: cmd/litestream/databases.go
type DatabasesCommand (line 12) | type DatabasesCommand struct
method Run (line 15) | func (c *DatabasesCommand) Run(_ context.Context, args []string) (err ...
method Usage (line 54) | func (c *DatabasesCommand) Usage() {
FILE: cmd/litestream/directory_watcher.go
constant debounceInterval (line 18) | debounceInterval = 250 * time.Millisecond
type DirectoryMonitor (line 22) | type DirectoryMonitor struct
method Close (line 104) | func (dm *DirectoryMonitor) Close() {
method run (line 110) | func (dm *DirectoryMonitor) run() {
method addInitialWatches (line 141) | func (dm *DirectoryMonitor) addInitialWatches() error {
method addDirectoryWatch (line 157) | func (dm *DirectoryMonitor) addDirectoryWatch(path string) error {
method removeDirectoryWatch (line 177) | func (dm *DirectoryMonitor) removeDirectoryWatch(path string) {
method handleEvent (line 195) | func (dm *DirectoryMonitor) handleEvent(event fsnotify.Event) bool {
method flushPendingEvents (line 258) | func (dm *DirectoryMonitor) flushPendingEvents() {
method handlePotentialDatabase (line 271) | func (dm *DirectoryMonitor) handlePotentialDatabase(path string) {
method removeDatabase (line 321) | func (dm *DirectoryMonitor) removeDatabase(path string) {
method removeDatabasesUnder (line 342) | func (dm *DirectoryMonitor) removeDatabasesUnder(dir string) {
method matchesPattern (line 374) | func (dm *DirectoryMonitor) matchesPattern(path string) bool {
method shouldSkipPath (line 385) | func (dm *DirectoryMonitor) shouldSkipPath(path string) bool {
method scanDirectory (line 394) | func (dm *DirectoryMonitor) scanDirectory(dir string) {
function NewDirectoryMonitor (line 47) | func NewDirectoryMonitor(ctx context.Context, store *litestream.Store, d...
FILE: cmd/litestream/directory_watcher_stress_test.go
function TestDirectoryWatcher_PreCreated (line 21) | func TestDirectoryWatcher_PreCreated(t *testing.T) {
function TestDirectoryWatcher_DynamicScaling (line 50) | func TestDirectoryWatcher_DynamicScaling(t *testing.T) {
function TestDirectoryWatcher_ConcurrentWrites (line 110) | func TestDirectoryWatcher_ConcurrentWrites(t *testing.T) {
function createTestDatabases (line 174) | func createTestDatabases(t *testing.T, dir string, count int) []*sql.DB {
function createTestDatabasesBatch (line 178) | func createTestDatabasesBatch(t *testing.T, dir string, startIdx, count ...
function closeTestDatabases (line 212) | func closeTestDatabases(dbs []*sql.DB) {
function startDirectoryMonitor (line 220) | func startDirectoryMonitor(t *testing.T, dbDir, replicaDir string) (*lit...
function stopDirectoryMonitor (line 257) | func stopDirectoryMonitor(store *litestream.Store, monitors []*Directory...
function waitForDBCount (line 264) | func waitForDBCount(ctx context.Context, store *litestream.Store, expect...
FILE: cmd/litestream/directory_watcher_test.go
function TestDirectoryMonitor_shouldSkipPath (line 9) | func TestDirectoryMonitor_shouldSkipPath(t *testing.T) {
function TestDirectoryMonitor_matchesPattern (line 47) | func TestDirectoryMonitor_matchesPattern(t *testing.T) {
function TestDirectoryMonitor_pendingEvents (line 83) | func TestDirectoryMonitor_pendingEvents(t *testing.T) {
FILE: cmd/litestream/info.go
type InfoCommand (line 17) | type InfoCommand struct
method Run (line 20) | func (c *InfoCommand) Run(_ context.Context, args []string) error {
method Usage (line 91) | func (c *InfoCommand) Usage() {
FILE: cmd/litestream/info_test.go
function testSocketPath (line 17) | func testSocketPath(t *testing.T) string {
function TestInfoCommand_Run (line 25) | func TestInfoCommand_Run(t *testing.T) {
FILE: cmd/litestream/list.go
type ListCommand (line 17) | type ListCommand struct
method Run (line 20) | func (c *ListCommand) Run(_ context.Context, args []string) error {
method Usage (line 96) | func (c *ListCommand) Usage() {
FILE: cmd/litestream/list_test.go
function TestListCommand_Run (line 12) | func TestListCommand_Run(t *testing.T) {
FILE: cmd/litestream/ltx.go
type LTXCommand (line 15) | type LTXCommand struct
method Run (line 18) | func (c *LTXCommand) Run(ctx context.Context, args []string) (err erro...
method Usage (line 110) | func (c *LTXCommand) Usage() {
FILE: cmd/litestream/ltx_test.go
function TestTXIDVarParsing (line 11) | func TestTXIDVarParsing(t *testing.T) {
function TestTXIDVarString (line 74) | func TestTXIDVarString(t *testing.T) {
function TestLevelVarParsing (line 112) | func TestLevelVarParsing(t *testing.T) {
function TestLevelVarString (line 185) | func TestLevelVarString(t *testing.T) {
FILE: cmd/litestream/main.go
type ConfigValidationError (line 62) | type ConfigValidationError struct
method Error (line 68) | func (e *ConfigValidationError) Error() string {
method Unwrap (line 75) | func (e *ConfigValidationError) Unwrap() error {
function main (line 79) | func main() {
type Main (line 92) | type Main struct
method Run (line 100) | func (m *Main) Run(ctx context.Context, args []string) (err error) {
method Usage (line 214) | func (m *Main) Usage() {
function NewMain (line 95) | func NewMain() *Main {
type Config (line 242) | type Config struct
method propagateGlobalSettings (line 323) | func (c *Config) propagateGlobalSettings() {
method Validate (line 362) | func (c *Config) Validate() error {
method CompactionLevels (line 481) | func (c *Config) CompactionLevels() litestream.CompactionLevels {
method DBConfig (line 497) | func (c *Config) DBConfig(configPath string) *DBConfig {
type SnapshotConfig (line 300) | type SnapshotConfig struct
type RetentionConfig (line 306) | type RetentionConfig struct
type ValidationConfig (line 311) | type ValidationConfig struct
type LoggingConfig (line 316) | type LoggingConfig struct
function DefaultConfig (line 336) | func DefaultConfig() Config {
function OpenConfigFile (line 508) | func OpenConfigFile(filename string) (io.ReadCloser, error) {
function ReadConfigFile (line 528) | func ReadConfigFile(filename string, expandEnv bool) (Config, error) {
function ParseConfig (line 540) | func ParseConfig(r io.Reader, expandEnv bool) (_ Config, err error) {
type CompactionLevelConfig (line 615) | type CompactionLevelConfig struct
type DBConfig (line 620) | type DBConfig struct
function NewDBFromConfig (line 640) | func NewDBFromConfig(dbc *DBConfig) (*litestream.DB, error) {
function NewDBsFromDirectoryConfig (line 703) | func NewDBsFromDirectoryConfig(dbc *DBConfig) ([]*litestream.DB, error) {
function newDBFromDirectoryEntry (line 752) | func newDBFromDirectoryEntry(dbc *DBConfig, dirPath, dbPath string) (*li...
function cloneReplicaConfigWithRelativePath (line 807) | func cloneReplicaConfigWithRelativePath(base *ReplicaConfig, relPath str...
function deriveMetaPathForDirectoryEntry (line 852) | func deriveMetaPathForDirectoryEntry(basePath, relPath string) string {
function appendRelativePathToURL (line 869) | func appendRelativePathToURL(u *url.URL, relPath string) {
function FindSQLiteDatabases (line 889) | func FindSQLiteDatabases(dir string, pattern string, recursive bool) ([]...
function IsSQLiteDatabase (line 927) | func IsSQLiteDatabase(path string) bool {
type ByteSize (line 946) | type ByteSize
method UnmarshalYAML (line 949) | func (b *ByteSize) UnmarshalYAML(unmarshal func(interface{}) error) er...
function ParseByteSize (line 966) | func ParseByteSize(s string) (int64, error) {
type ReplicaSettings (line 988) | type ReplicaSettings struct
method SetDefaults (line 1060) | func (rs *ReplicaSettings) SetDefaults(src *ReplicaSettings) {
type ReplicaConfig (line 1200) | type ReplicaConfig struct
method ReplicaType (line 1890) | func (c *ReplicaConfig) ReplicaType() string {
function NewReplicaFromConfig (line 1210) | func NewReplicaFromConfig(c *ReplicaConfig, db *litestream.DB) (_ *lites...
function newFileReplicaClientFromConfig (line 1278) | func newFileReplicaClientFromConfig(c *ReplicaConfig, r *litestream.Repl...
function NewS3ReplicaClientFromConfig (line 1310) | func NewS3ReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replic...
function newGSReplicaClientFromConfig (line 1524) | func newGSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Replic...
function newABSReplicaClientFromConfig (line 1563) | func newABSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Repli...
function newSFTPReplicaClientFromConfig (line 1607) | func newSFTPReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Repl...
function newWebDAVReplicaClientFromConfig (line 1664) | func newWebDAVReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Re...
function newNATSReplicaClientFromConfig (line 1718) | func newNATSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Repl...
function newOSSReplicaClientFromConfig (line 1791) | func newOSSReplicaClientFromConfig(c *ReplicaConfig, _ *litestream.Repli...
function applyLitestreamEnv (line 1856) | func applyLitestreamEnv() {
type boolSetting (line 1869) | type boolSetting struct
method Set (line 1878) | func (s *boolSetting) Set(value bool) {
method ApplyDefault (line 1883) | func (s *boolSetting) ApplyDefault(value bool) {
function newBoolSetting (line 1874) | func newBoolSetting(defaultValue bool) boolSetting {
function DefaultConfigPath (line 1900) | func DefaultConfigPath() string {
function registerConfigFlag (line 1907) | func registerConfigFlag(fs *flag.FlagSet) (configPath *string, noExpandE...
function isValidHeartbeatURL (line 1913) | func isValidHeartbeatURL(u string) bool {
function expand (line 1919) | func expand(s string) (string, error) {
function StripSQLitePrefix (line 1947) | func StripSQLitePrefix(s string) string {
type txidVar (line 1960) | type txidVar
method String (line 1966) | func (v *txidVar) String() string {
method Set (line 1971) | func (v *txidVar) Set(s string) error {
constant levelAll (line 1981) | levelAll = -1
type levelVar (line 1985) | type levelVar
method String (line 1989) | func (v *levelVar) String() string {
method Set (line 1996) | func (v *levelVar) Set(s string) error {
function initLog (line 2012) | func initLog(w io.Writer, level, typ string) {
FILE: cmd/litestream/main_notwindows.go
constant defaultConfigPath (line 12) | defaultConfigPath = "/etc/litestream.yml"
function isWindowsService (line 14) | func isWindowsService() (bool, error) {
function runWindowsService (line 18) | func runWindowsService(ctx context.Context) error {
function signalChan (line 22) | func signalChan() <-chan os.Signal {
FILE: cmd/litestream/main_test.go
function TestOpenConfigFile (line 25) | func TestOpenConfigFile(t *testing.T) {
function TestReadConfigFile (line 63) | func TestReadConfigFile(t *testing.T) {
function TestNewDBFromConfig_MetaPathExpansion (line 144) | func TestNewDBFromConfig_MetaPathExpansion(t *testing.T) {
function TestNewFileReplicaFromConfig (line 184) | func TestNewFileReplicaFromConfig(t *testing.T) {
function TestNewS3ReplicaFromConfig (line 195) | func TestNewS3ReplicaFromConfig(t *testing.T) {
function TestNewGSReplicaFromConfig (line 308) | func TestNewGSReplicaFromConfig(t *testing.T) {
function TestNewSFTPReplicaFromConfig (line 321) | func TestNewSFTPReplicaFromConfig(t *testing.T) {
function TestNewReplicaFromConfig_AgeEncryption (line 347) | func TestNewReplicaFromConfig_AgeEncryption(t *testing.T) {
function TestConfig_Validate_SnapshotIntervals (line 416) | func TestConfig_Validate_SnapshotIntervals(t *testing.T) {
function TestConfig_Validate_ValidationInterval (line 542) | func TestConfig_Validate_ValidationInterval(t *testing.T) {
function TestParseReplicaURL_AccessPoint (line 578) | func TestParseReplicaURL_AccessPoint(t *testing.T) {
function TestConfig_Validate_L0Retention (line 602) | func TestConfig_Validate_L0Retention(t *testing.T) {
function TestConfig_Validate_SyncIntervals (line 664) | func TestConfig_Validate_SyncIntervals(t *testing.T) {
function TestConfig_Validate_CompactionLevels (line 813) | func TestConfig_Validate_CompactionLevels(t *testing.T) {
function TestConfig_DefaultValues (line 924) | func TestConfig_DefaultValues(t *testing.T) {
function TestParseByteSize (line 947) | func TestParseByteSize(t *testing.T) {
function TestParseByteSizeOverflow (line 1023) | func TestParseByteSizeOverflow(t *testing.T) {
function TestS3ReplicaConfig_PartSizeAndConcurrency (line 1038) | func TestS3ReplicaConfig_PartSizeAndConcurrency(t *testing.T) {
function TestDBConfig_CheckpointFields (line 1307) | func TestDBConfig_CheckpointFields(t *testing.T) {
function TestFindSQLiteDatabases (line 1482) | func TestFindSQLiteDatabases(t *testing.T) {
function TestParseReplicaURLWithQuery (line 1579) | func TestParseReplicaURLWithQuery(t *testing.T) {
function TestIsSQLiteDatabase (line 1699) | func TestIsSQLiteDatabase(t *testing.T) {
function TestDBConfigValidation (line 1747) | func TestDBConfigValidation(t *testing.T) {
function TestNewDBsFromDirectoryConfig_UniquePaths (line 1825) | func TestNewDBsFromDirectoryConfig_UniquePaths(t *testing.T) {
function TestNewDBsFromDirectoryConfig_MetaPathPerDatabase (line 1879) | func TestNewDBsFromDirectoryConfig_MetaPathPerDatabase(t *testing.T) {
function TestNewDBsFromDirectoryConfig_SubdirectoryPaths (line 1946) | func TestNewDBsFromDirectoryConfig_SubdirectoryPaths(t *testing.T) {
function TestNewDBsFromDirectoryConfig_DuplicateFilenames (line 1996) | func TestNewDBsFromDirectoryConfig_DuplicateFilenames(t *testing.T) {
function TestNewDBsFromDirectoryConfig_S3URL (line 2051) | func TestNewDBsFromDirectoryConfig_S3URL(t *testing.T) {
function TestNewDBsFromDirectoryConfig_ReplicasArrayURL (line 2096) | func TestNewDBsFromDirectoryConfig_ReplicasArrayURL(t *testing.T) {
function TestNewDBsFromDirectoryConfig_SpecialCharacters (line 2143) | func TestNewDBsFromDirectoryConfig_SpecialCharacters(t *testing.T) {
function TestNewDBsFromDirectoryConfig_EmptyBasePath (line 2188) | func TestNewDBsFromDirectoryConfig_EmptyBasePath(t *testing.T) {
function TestNewDBsFromDirectoryConfig_ReplicasArray (line 2221) | func TestNewDBsFromDirectoryConfig_ReplicasArray(t *testing.T) {
function TestNewDBsFromDirectoryConfig_EmptyDirectoryRequiresDatabases (line 2261) | func TestNewDBsFromDirectoryConfig_EmptyDirectoryRequiresDatabases(t *te...
function TestNewDBsFromDirectoryConfig_EmptyDirectoryWithWatch (line 2276) | func TestNewDBsFromDirectoryConfig_EmptyDirectoryWithWatch(t *testing.T) {
function TestDirectoryMonitor_DetectsDatabaseLifecycle (line 2296) | func TestDirectoryMonitor_DetectsDatabaseLifecycle(t *testing.T) {
function TestDirectoryMonitor_RecursiveDetectsNestedDatabases (line 2351) | func TestDirectoryMonitor_RecursiveDetectsNestedDatabases(t *testing.T) {
function createSQLiteDB (line 2395) | func createSQLiteDB(t *testing.T, path string) {
function TestNewS3ReplicaClientFromConfig (line 2415) | func TestNewS3ReplicaClientFromConfig(t *testing.T) {
function TestGlobalDefaults (line 2702) | func TestGlobalDefaults(t *testing.T) {
function TestStripSQLitePrefix (line 2945) | func TestStripSQLitePrefix(t *testing.T) {
function TestReadConfigFile_SQLiteConnectionString (line 2974) | func TestReadConfigFile_SQLiteConnectionString(t *testing.T) {
function TestX509FallbackRoots (line 3032) | func TestX509FallbackRoots(t *testing.T) {
function hasDBPath (line 3062) | func hasDBPath(dbs []*litestream.DB, path string) bool {
function waitForCondition (line 3071) | func waitForCondition(timeout time.Duration, fn func() bool) bool {
FILE: cmd/litestream/main_windows.go
constant defaultConfigPath (line 17) | defaultConfigPath = `C:\Litestream\litestream.yml`
constant serviceName (line 20) | serviceName = "Litestream"
function isWindowsService (line 23) | func isWindowsService() (bool, error) {
function runWindowsService (line 27) | func runWindowsService(ctx context.Context) error {
type windowsService (line 53) | type windowsService struct
method Execute (line 57) | func (s *windowsService) Execute(args []string, r <-chan svc.ChangeReq...
type eventlogWriter (line 101) | type eventlogWriter
method Write (line 103) | func (w *eventlogWriter) Write(p []byte) (n int, err error) {
function signalChan (line 108) | func signalChan() <-chan os.Signal {
FILE: cmd/litestream/mcp.go
type MCPServer (line 18) | type MCPServer struct
method Start (line 52) | func (s *MCPServer) Start(addr string) {
method ServeHTTP (line 66) | func (s *MCPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
method Close (line 71) | func (s *MCPServer) Close() error {
function NewMCP (line 25) | func NewMCP(ctx context.Context, configPath string) (*MCPServer, error) {
function isReplicaURL (line 79) | func isReplicaURL(path string) bool {
function DatabasesTool (line 83) | func DatabasesTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
function InfoTool (line 105) | func InfoTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
function RestoreTool (line 183) | func RestoreTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
function VersionTool (line 244) | func VersionTool() (mcp.Tool, server.ToolHandlerFunc) {
function LTXTool (line 259) | func LTXTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
function StatusTool (line 296) | func StatusTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
function ResetTool (line 322) | func ResetTool(configPath string) (mcp.Tool, server.ToolHandlerFunc) {
FILE: cmd/litestream/register.go
type RegisterCommand (line 17) | type RegisterCommand struct
method Run (line 19) | func (c *RegisterCommand) Run(ctx context.Context, args []string) error {
method Usage (line 98) | func (c *RegisterCommand) Usage() {
FILE: cmd/litestream/register_test.go
function TestRegisterCommand_Run (line 13) | func TestRegisterCommand_Run(t *testing.T) {
FILE: cmd/litestream/replicate.go
type ReplicateCommand (line 30) | type ReplicateCommand struct
method ParseFlags (line 65) | func (c *ReplicateCommand) ParseFlags(_ context.Context, args []string...
method Run (line 177) | func (c *ReplicateCommand) Run(ctx context.Context) (err error) {
method runOnce (line 393) | func (c *ReplicateCommand) runOnce(ctx context.Context) {
method Close (line 435) | func (c *ReplicateCommand) Close(ctx context.Context) error {
method SetDone (line 465) | func (c *ReplicateCommand) SetDone(done <-chan struct{}) {
method restoreIfNeeded (line 472) | func (c *ReplicateCommand) restoreIfNeeded(ctx context.Context, dbConf...
method Usage (line 518) | func (c *ReplicateCommand) Usage() {
function NewReplicateCommand (line 58) | func NewReplicateCommand() *ReplicateCommand {
FILE: cmd/litestream/replicate_test.go
function TestReplicateCommand_ParseFlags_OnceFlags (line 12) | func TestReplicateCommand_ParseFlags_OnceFlags(t *testing.T) {
function TestReplicateCommand_ParseFlags_FlagPositioning (line 89) | func TestReplicateCommand_ParseFlags_FlagPositioning(t *testing.T) {
function TestReplicateCommand_ParseFlags_LogLevel (line 177) | func TestReplicateCommand_ParseFlags_LogLevel(t *testing.T) {
FILE: cmd/litestream/reset.go
type ResetCommand (line 14) | type ResetCommand struct
method Run (line 17) | func (c *ResetCommand) Run(ctx context.Context, args []string) (err er...
method Usage (line 101) | func (c *ResetCommand) Usage() {
FILE: cmd/litestream/restore.go
type RestoreCommand (line 17) | type RestoreCommand struct
method Run (line 20) | func (c *RestoreCommand) Run(ctx context.Context, args []string) (err ...
method loadFromURL (line 116) | func (c *RestoreCommand) loadFromURL(ctx context.Context, replicaURL s...
method loadFromConfig (line 141) | func (c *RestoreCommand) loadFromConfig(_ context.Context, dbPath, con...
method Usage (line 175) | func (c *RestoreCommand) Usage() {
FILE: cmd/litestream/restore_test.go
function TestRestoreCommand_FollowIntervalFlag (line 11) | func TestRestoreCommand_FollowIntervalFlag(t *testing.T) {
FILE: cmd/litestream/start.go
type StartCommand (line 18) | type StartCommand struct
method Run (line 21) | func (c *StartCommand) Run(ctx context.Context, args []string) error {
method Usage (line 93) | func (c *StartCommand) Usage() {
FILE: cmd/litestream/status.go
type StatusCommand (line 16) | type StatusCommand struct
method Run (line 19) | func (c *StatusCommand) Run(ctx context.Context, args []string) (err e...
method getDBStatus (line 79) | func (c *StatusCommand) getDBStatus(db *litestream.DB) DBStatus {
method Usage (line 116) | func (c *StatusCommand) Usage() {
type DBStatus (line 72) | type DBStatus struct
FILE: cmd/litestream/status_test.go
function TestStatusCommand_Run (line 12) | func TestStatusCommand_Run(t *testing.T) {
FILE: cmd/litestream/stop.go
type StopCommand (line 18) | type StopCommand struct
method Run (line 21) | func (c *StopCommand) Run(ctx context.Context, args []string) error {
method Usage (line 93) | func (c *StopCommand) Usage() {
FILE: cmd/litestream/sync.go
type SyncCommand (line 18) | type SyncCommand struct
method Run (line 21) | func (c *SyncCommand) Run(ctx context.Context, args []string) error {
method Usage (line 97) | func (c *SyncCommand) Usage() {
FILE: cmd/litestream/sync_test.go
function TestSyncCommand_Run (line 12) | func TestSyncCommand_Run(t *testing.T) {
FILE: cmd/litestream/unregister.go
type UnregisterCommand (line 17) | type UnregisterCommand struct
method Run (line 19) | func (c *UnregisterCommand) Run(ctx context.Context, args []string) er...
method Usage (line 93) | func (c *UnregisterCommand) Usage() {
FILE: cmd/litestream/unregister_test.go
function TestUnregisterCommand_Run (line 12) | func TestUnregisterCommand_Run(t *testing.T) {
FILE: cmd/litestream/version.go
type VersionCommand (line 10) | type VersionCommand struct
method Run (line 13) | func (c *VersionCommand) Run(_ context.Context, args []string) (err er...
method Usage (line 26) | func (c *VersionCommand) Usage() {
FILE: compaction_level.go
constant SnapshotLevel (line 9) | SnapshotLevel = 9
type CompactionLevel (line 23) | type CompactionLevel struct
method PrevCompactionAt (line 33) | func (lvl *CompactionLevel) PrevCompactionAt(now time.Time) time.Time {
method NextCompactionAt (line 39) | func (lvl *CompactionLevel) NextCompactionAt(now time.Time) time.Time {
type CompactionLevels (line 44) | type CompactionLevels
method Level (line 48) | func (a CompactionLevels) Level(level int) (*CompactionLevel, error) {
method MaxLevel (line 59) | func (a CompactionLevels) MaxLevel() int {
method Validate (line 64) | func (a CompactionLevels) Validate() error {
method IsValidLevel (line 88) | func (a CompactionLevels) IsValidLevel(level int) bool {
method PrevLevel (line 97) | func (a CompactionLevels) PrevLevel(level int) int {
method NextLevel (line 106) | func (a CompactionLevels) NextLevel(level int) int {
FILE: compactor.go
type Compactor (line 18) | type Compactor struct
method setLogger (line 66) | func (c *Compactor) setLogger(logger *slog.Logger) {
method MaxLTXFileInfo (line 72) | func (c *Compactor) MaxLTXFileInfo(ctx context.Context, level int) (lt...
method Compact (line 102) | func (c *Compactor) Compact(ctx context.Context, dstLevel int) (*ltx.F...
method VerifyLevelConsistency (line 194) | func (c *Compactor) VerifyLevelConsistency(ctx context.Context, level ...
method EnforceSnapshotRetention (line 235) | func (c *Compactor) EnforceSnapshotRetention(ctx context.Context, rete...
method EnforceRetentionByTXID (line 290) | func (c *Compactor) EnforceRetentionByTXID(ctx context.Context, level ...
method EnforceL0Retention (line 339) | func (c *Compactor) EnforceL0Retention(ctx context.Context, retention ...
function NewCompactor (line 55) | func NewCompactor(client ReplicaClient, logger *slog.Logger) *Compactor {
FILE: compactor_test.go
function TestCompactor_Compact (line 17) | func TestCompactor_Compact(t *testing.T) {
function TestCompactor_MaxLTXFileInfo (line 88) | func TestCompactor_MaxLTXFileInfo(t *testing.T) {
function TestCompactor_EnforceRetentionByTXID (line 155) | func TestCompactor_EnforceRetentionByTXID(t *testing.T) {
function TestCompactor_EnforceL0Retention (line 220) | func TestCompactor_EnforceL0Retention(t *testing.T) {
function TestCompactor_EnforceSnapshotRetention (line 290) | func TestCompactor_EnforceSnapshotRetention(t *testing.T) {
function TestCompactor_EnforceSnapshotRetention_RetentionDisabled (line 324) | func TestCompactor_EnforceSnapshotRetention_RetentionDisabled(t *testing...
function TestCompactor_EnforceRetentionByTXID_RetentionDisabled (line 365) | func TestCompactor_EnforceRetentionByTXID_RetentionDisabled(t *testing.T) {
function TestCompactor_EnforceL0Retention_RetentionDisabled (line 406) | func TestCompactor_EnforceL0Retention_RetentionDisabled(t *testing.T) {
function TestCompactor_VerifyLevelConsistency (line 456) | func TestCompactor_VerifyLevelConsistency(t *testing.T) {
function TestCompactor_CompactWithVerification (line 532) | func TestCompactor_CompactWithVerification(t *testing.T) {
function containsString (line 558) | func containsString(s, substr string) bool {
function createTestLTXFile (line 563) | func createTestLTXFile(t testing.TB, client litestream.ReplicaClient, le...
function createTestLTXFileWithTimestamp (line 569) | func createTestLTXFileWithTimestamp(t testing.TB, client litestream.Repl...
FILE: db.go
constant DefaultMonitorInterval (line 31) | DefaultMonitorInterval = 1 * time.Second
constant DefaultCheckpointInterval (line 32) | DefaultCheckpointInterval = 1 * time.Minute
constant DefaultBusyTimeout (line 33) | DefaultBusyTimeout = 1 * time.Second
constant DefaultMinCheckpointPageN (line 34) | DefaultMinCheckpointPageN = 1000
constant DefaultTruncatePageN (line 35) | DefaultTruncatePageN = 121359
constant DefaultShutdownSyncTimeout (line 36) | DefaultShutdownSyncTimeout = 30 * time.Second
constant DefaultShutdownSyncInterval (line 37) | DefaultShutdownSyncInterval = 500 * time.Millisecond
constant DefaultSyncBackoffMax (line 41) | DefaultSyncBackoffMax = 5 * time.Minute
constant SyncErrorLogInterval (line 42) | SyncErrorLogInterval = 30 * time.Second
type DB (line 64) | type DB struct
method SetLogger (line 256) | func (db *DB) SetLogger(logger *slog.Logger) {
method SQLDB (line 270) | func (db *DB) SQLDB() *sql.DB {
method Path (line 275) | func (db *DB) Path() string {
method IsOpen (line 280) | func (db *DB) IsOpen() bool {
method WALPath (line 287) | func (db *DB) WALPath() string {
method MetaPath (line 292) | func (db *DB) MetaPath() string {
method SetMetaPath (line 297) | func (db *DB) SetMetaPath(path string) {
method LTXDir (line 302) | func (db *DB) LTXDir() string {
method ResetLocalState (line 309) | func (db *DB) ResetLocalState(ctx context.Context) error {
method LTXLevelDir (line 332) | func (db *DB) LTXLevelDir(level int) string {
method LTXPath (line 338) | func (db *DB) LTXPath(level int, minTXID, maxTXID ltx.TXID) string {
method openLocalLTXFile (line 345) | func (db *DB) openLocalLTXFile(level int, minTXID, maxTXID ltx.TXID) (...
method deleteLocalLTXFile (line 351) | func (db *DB) deleteLocalLTXFile(level int, minTXID, maxTXID ltx.TXID)...
method MaxLTX (line 363) | func (db *DB) MaxLTX() (minTXID, maxTXID ltx.TXID, err error) {
method FileInfo (line 383) | func (db *DB) FileInfo() os.FileInfo {
method DirInfo (line 388) | func (db *DB) DirInfo() os.FileInfo {
method Pos (line 394) | func (db *DB) Pos() (ltx.Pos, error) {
method invalidatePosCache (line 429) | func (db *DB) invalidatePosCache() {
method Notify (line 436) | func (db *DB) Notify() <-chan struct{} {
method PageSize (line 444) | func (db *DB) PageSize() int {
method RecordSuccessfulSync (line 452) | func (db *DB) RecordSuccessfulSync() {
method LastSuccessfulSyncAt (line 459) | func (db *DB) LastSuccessfulSyncAt() time.Time {
method SyncStatus (line 475) | func (db *DB) SyncStatus(ctx context.Context) (SyncStatus, error) {
method SyncAndWait (line 499) | func (db *DB) SyncAndWait(ctx context.Context) error {
method EnsureExists (line 516) | func (db *DB) EnsureExists(ctx context.Context) error {
method Open (line 553) | func (db *DB) Open() (err error) {
method Close (line 601) | func (db *DB) Close(ctx context.Context) (err error) {
method syncReplicaWithRetry (line 655) | func (db *DB) syncReplicaWithRetry(ctx context.Context) error {
method setPersistWAL (line 772) | func (db *DB) setPersistWAL(ctx context.Context) error {
method init (line 796) | func (db *DB) init(ctx context.Context) (err error) {
method acquireReadLock (line 955) | func (db *DB) acquireReadLock(ctx context.Context) error {
method releaseReadLock (line 978) | func (db *DB) releaseReadLock() error {
method Sync (line 994) | func (db *DB) Sync(ctx context.Context) (err error) {
method verifyAndSync (line 1057) | func (db *DB) verifyAndSync(ctx context.Context, checkpointing bool) (...
method checkpointIfNeeded (line 1100) | func (db *DB) checkpointIfNeeded(ctx context.Context, origWALSize, new...
method walFileSize (line 1182) | func (db *DB) walFileSize() (int64, error) {
method ensureWALExists (line 1199) | func (db *DB) ensureWALExists(ctx context.Context) (err error) {
method checkDatabaseBehindReplica (line 1216) | func (db *DB) checkDatabaseBehindReplica(ctx context.Context) error {
method verify (line 1297) | func (db *DB) verify(ctx context.Context) (info syncInfo, err error) {
method lastPageMatch (line 1441) | func (db *DB) lastPageMatch(ctx context.Context, dec *ltx.Decoder, pre...
method detectFullCheckpoint (line 1481) | func (db *DB) detectFullCheckpoint(ctx context.Context, knownSalts [][...
method sync (line 1521) | func (db *DB) sync(ctx context.Context, checkpointing bool, info syncI...
method writeLTXFromDB (line 1727) | func (db *DB) writeLTXFromDB(ctx context.Context, enc *ltx.Encoder, wa...
method writeLTXFromWAL (line 1774) | func (db *DB) writeLTXFromWAL(ctx context.Context, enc *ltx.Encoder, w...
method Checkpoint (line 1804) | func (db *DB) Checkpoint(ctx context.Context, mode string) (err error) {
method checkpoint (line 1812) | func (db *DB) checkpoint(ctx context.Context, mode string) error {
method execCheckpoint (line 1877) | func (db *DB) execCheckpoint(ctx context.Context, mode string) (err er...
method SnapshotReader (line 1924) | func (db *DB) SnapshotReader(ctx context.Context) (ltx.Pos, io.Reader,...
method Compact (line 2028) | func (db *DB) Compact(ctx context.Context, dstLevel int) (*ltx.FileInf...
method Snapshot (line 2048) | func (db *DB) Snapshot(ctx context.Context) (*ltx.FileInfo, error) {
method EnforceSnapshotRetention (line 2066) | func (db *DB) EnforceSnapshotRetention(ctx context.Context, timestamp ...
method EnforceL0RetentionByTime (line 2122) | func (db *DB) EnforceL0RetentionByTime(ctx context.Context) error {
method EnforceRetentionByTXID (line 2248) | func (db *DB) EnforceRetentionByTXID(ctx context.Context, level int, t...
method monitor (line 2256) | func (db *DB) monitor() {
method CRC64 (line 2331) | func (db *DB) CRC64(ctx context.Context) (uint64, ltx.Pos, error) {
method MaxLTXFileInfo (line 2365) | func (db *DB) MaxLTXFileInfo(ctx context.Context, level int) (ltx.File...
function NewDB (line 201) | func NewDB(path string) *DB {
type SyncStatus (line 466) | type SyncStatus struct
function isSQLiteBusyError (line 1158) | func isSQLiteBusyError(err error) bool {
function isDiskFullError (line 1170) | func isDiskFullError(err error) bool {
function calcWALSize (line 1194) | func calcWALSize(pageSize uint32, pageN uint32) int64 {
type syncInfo (line 1511) | type syncInfo struct
constant DefaultRestoreParallelism (line 2384) | DefaultRestoreParallelism = 8
constant DefaultFollowInterval (line 2387) | DefaultFollowInterval = 1 * time.Second
type IntegrityCheckMode (line 2390) | type IntegrityCheckMode
constant IntegrityCheckNone (line 2393) | IntegrityCheckNone IntegrityCheckMode = iota
constant IntegrityCheckQuick (line 2394) | IntegrityCheckQuick
constant IntegrityCheckFull (line 2395) | IntegrityCheckFull
type RestoreOptions (line 2399) | type RestoreOptions struct
function NewRestoreOptions (line 2428) | func NewRestoreOptions() RestoreOptions {
FILE: db_internal_test.go
type testReplicaClient (line 25) | type testReplicaClient struct
method Init (line 29) | func (c *testReplicaClient) Init(_ context.Context) error { return nil }
method SetLogger (line 31) | func (c *testReplicaClient) SetLogger(_ *slog.Logger) {}
method Type (line 33) | func (c *testReplicaClient) Type() string { return "test" }
method LTXFiles (line 35) | func (c *testReplicaClient) LTXFiles(_ context.Context, level int, aft...
method OpenLTXFile (line 70) | func (c *testReplicaClient) OpenLTXFile(_ context.Context, level int, ...
method WriteLTXFile (line 77) | func (c *testReplicaClient) WriteLTXFile(_ context.Context, level int,...
method DeleteLTXFiles (line 97) | func (c *testReplicaClient) DeleteLTXFiles(_ context.Context, infos []...
method DeleteAll (line 102) | func (c *testReplicaClient) DeleteAll(_ context.Context) error {
function TestCalcWALSize (line 109) | func TestCalcWALSize(t *testing.T) {
function TestDB_Sync_UpdatesMetrics (line 177) | func TestDB_Sync_UpdatesMetrics(t *testing.T) {
function TestDB_Checkpoint_UpdatesMetrics (line 273) | func TestDB_Checkpoint_UpdatesMetrics(t *testing.T) {
function TestDB_ReplicaSync_OperationMetrics (line 339) | func TestDB_ReplicaSync_OperationMetrics(t *testing.T) {
function TestDB_Sync_ErrorMetrics (line 400) | func TestDB_Sync_ErrorMetrics(t *testing.T) {
function TestDB_Checkpoint_ErrorMetrics (line 454) | func TestDB_Checkpoint_ErrorMetrics(t *testing.T) {
function TestDB_L0RetentionMetrics (line 500) | func TestDB_L0RetentionMetrics(t *testing.T) {
function TestDB_Verify_WALOffsetAtHeader (line 563) | func TestDB_Verify_WALOffsetAtHeader(t *testing.T) {
function TestDB_Verify_WALOffsetAtHeader_SaltMismatch (line 685) | func TestDB_Verify_WALOffsetAtHeader_SaltMismatch(t *testing.T) {
function TestDB_releaseReadLock_DoubleRollback (line 810) | func TestDB_releaseReadLock_DoubleRollback(t *testing.T) {
function TestDB_CheckpointDoesNotTriggerSnapshot (line 875) | func TestDB_CheckpointDoesNotTriggerSnapshot(t *testing.T) {
function testCheckpointSnapshot (line 884) | func testCheckpointSnapshot(t *testing.T, mode string) {
function TestDB_MultipleCheckpointsWithWrites (line 987) | func TestDB_MultipleCheckpointsWithWrites(t *testing.T) {
function TestIsDiskFullError (line 1062) | func TestIsDiskFullError(t *testing.T) {
function TestIsSQLiteBusyError (line 1126) | func TestIsSQLiteBusyError(t *testing.T) {
function TestDB_IdleCheckpointSnapshotLoop (line 1176) | func TestDB_IdleCheckpointSnapshotLoop(t *testing.T) {
function TestDB_Issue994_RunawayDiskUsage (line 1260) | func TestDB_Issue994_RunawayDiskUsage(t *testing.T) {
function dirSize (line 1350) | func dirSize(t *testing.T, path string) int64 {
function dirFileCount (line 1368) | func dirFileCount(t *testing.T, path string) int {
function TestDB_WALPageCoverage_AllNewPagesPresent (line 1394) | func TestDB_WALPageCoverage_AllNewPagesPresent(t *testing.T) {
function TestDB_WriteLTXFromWAL_PageGrowthCoverage (line 1462) | func TestDB_WriteLTXFromWAL_PageGrowthCoverage(t *testing.T) {
function TestDB_Sync_CompactionValidAfterGrowthAndCheckpoint (line 1584) | func TestDB_Sync_CompactionValidAfterGrowthAndCheckpoint(t *testing.T) {
function TestDB_Sync_InitErrorMetrics (line 1691) | func TestDB_Sync_InitErrorMetrics(t *testing.T) {
FILE: db_shutdown_test.go
function TestDB_Close_SyncRetry (line 19) | func TestDB_Close_SyncRetry(t *testing.T) {
FILE: db_test.go
function TestDB_Path (line 22) | func TestDB_Path(t *testing.T) {
function TestDB_WALPath (line 29) | func TestDB_WALPath(t *testing.T) {
function TestDB_MetaPath (line 36) | func TestDB_MetaPath(t *testing.T) {
function TestDB_CRC64 (line 52) | func TestDB_CRC64(t *testing.T) {
function TestDB_Sync (line 106) | func TestDB_Sync(t *testing.T) {
function TestDB_Compact (line 385) | func TestDB_Compact(t *testing.T) {
function walPageCountForTest (line 488) | func walPageCountForTest(tb testing.TB, db *litestream.DB) int64 {
function TestDB_Snapshot (line 508) | func TestDB_Snapshot(t *testing.T) {
function TestDB_EnforceRetention (line 553) | func TestDB_EnforceRetention(t *testing.T) {
function TestDB_EnforceSnapshotRetention_RetentionDisabled (line 625) | func TestDB_EnforceSnapshotRetention_RetentionDisabled(t *testing.T) {
function TestDB_EnforceL0RetentionByTime_RetentionDisabled (line 685) | func TestDB_EnforceL0RetentionByTime_RetentionDisabled(t *testing.T) {
function TestDB_ConcurrentMapWrite (line 757) | func TestDB_ConcurrentMapWrite(t *testing.T) {
function TestCompaction_PreservesLastTimestamp (line 833) | func TestCompaction_PreservesLastTimestamp(t *testing.T) {
function TestDB_EnforceRetentionByTXID_LocalCleanup (line 947) | func TestDB_EnforceRetentionByTXID_LocalCleanup(t *testing.T) {
function TestDB_EnforceL0RetentionByTime (line 1042) | func TestDB_EnforceL0RetentionByTime(t *testing.T) {
function TestDB_SyncAfterVacuum (line 1152) | func TestDB_SyncAfterVacuum(t *testing.T) {
function TestDB_NoLTXFilesOnIdleSync (line 1219) | func TestDB_NoLTXFilesOnIdleSync(t *testing.T) {
function TestDB_DelayedCheckpointAfterWrite (line 1287) | func TestDB_DelayedCheckpointAfterWrite(t *testing.T) {
function TestDB_SyncStatus (line 1353) | func TestDB_SyncStatus(t *testing.T) {
function TestDB_SyncAndWait (line 1485) | func TestDB_SyncAndWait(t *testing.T) {
function TestDB_EnsureExists (line 1519) | func TestDB_EnsureExists(t *testing.T) {
function TestDB_ResetLocalState (line 1660) | func TestDB_ResetLocalState(t *testing.T) {
FILE: file/replica_client.go
function init (line 21) | func init() {
constant ReplicaClientType (line 26) | ReplicaClientType = "file"
type ReplicaClient (line 32) | type ReplicaClient struct
method SetLogger (line 47) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method db (line 62) | func (c *ReplicaClient) db() *litestream.DB {
method Type (line 70) | func (c *ReplicaClient) Type() string {
method Init (line 75) | func (c *ReplicaClient) Init(ctx context.Context) error {
method Path (line 80) | func (c *ReplicaClient) Path() string {
method LTXLevelDir (line 85) | func (c *ReplicaClient) LTXLevelDir(level int) string {
method LTXFilePath (line 90) | func (c *ReplicaClient) LTXFilePath(level int, minTXID, maxTXID ltx.TX...
method LTXFiles (line 96) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method OpenLTXFile (line 135) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method WriteLTXFile (line 160) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method DeleteLTXFiles (line 240) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method DeleteAll (line 254) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method GenerationsV3 (line 262) | func (c *ReplicaClient) GenerationsV3(ctx context.Context) ([]string, ...
method SnapshotsV3 (line 282) | func (c *ReplicaClient) SnapshotsV3(ctx context.Context, generation st...
method WALSegmentsV3 (line 318) | func (c *ReplicaClient) WALSegmentsV3(ctx context.Context, generation ...
method OpenSnapshotV3 (line 359) | func (c *ReplicaClient) OpenSnapshotV3(ctx context.Context, generation...
method OpenWALSegmentV3 (line 370) | func (c *ReplicaClient) OpenWALSegmentV3(ctx context.Context, generati...
function NewReplicaClient (line 40) | func NewReplicaClient(path string) *ReplicaClient {
function NewReplicaClientFromURL (line 53) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
FILE: file/replica_client_test.go
function TestReplicaClient_Path (line 20) | func TestReplicaClient_Path(t *testing.T) {
function TestReplicaClient_Type (line 27) | func TestReplicaClient_Type(t *testing.T) {
function TestReplicaClient_WriteLTXFile_ErrorCleanup (line 34) | func TestReplicaClient_WriteLTXFile_ErrorCleanup(t *testing.T) {
type failAfterReader (line 114) | type failAfterReader struct
method Read (line 121) | func (r *failAfterReader) Read(p []byte) (n int, err error) {
function findTmpFiles (line 142) | func findTmpFiles(t *testing.T, root string) []string {
function createLTXData (line 161) | func createLTXData(minTXID, maxTXID ltx.TXID, data []byte) []byte {
function createLTXHeader (line 180) | func createLTXHeader(minTXID, maxTXID ltx.TXID) []byte {
function TestReplicaClient_GenerationsV3 (line 184) | func TestReplicaClient_GenerationsV3(t *testing.T) {
function TestReplicaClient_SnapshotsV3 (line 298) | func TestReplicaClient_SnapshotsV3(t *testing.T) {
function TestReplicaClient_WALSegmentsV3 (line 422) | func TestReplicaClient_WALSegmentsV3(t *testing.T) {
function TestReplicaClient_OpenSnapshotV3 (line 568) | func TestReplicaClient_OpenSnapshotV3(t *testing.T) {
function TestReplicaClient_OpenWALSegmentV3 (line 622) | func TestReplicaClient_OpenWALSegmentV3(t *testing.T) {
FILE: gs/replica_client.go
function init (line 24) | func init() {
constant ReplicaClientType (line 29) | ReplicaClientType = "gs"
constant MetadataKeyTimestamp (line 32) | MetadataKeyTimestamp = "litestream-timestamp"
type ReplicaClient (line 37) | type ReplicaClient struct
method SetLogger (line 55) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 73) | func (c *ReplicaClient) Type() string {
method Init (line 78) | func (c *ReplicaClient) Init(ctx context.Context) (err error) {
method DeleteAll (line 95) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method LTXFiles (line 126) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method WriteLTXFile (line 141) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method OpenLTXFile (line 191) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method DeleteLTXFiles (line 220) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
function NewReplicaClient (line 49) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 61) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
type ltxFileIterator (line 239) | type ltxFileIterator struct
method Close (line 255) | func (itr *ltxFileIterator) Close() (err error) {
method Next (line 259) | func (itr *ltxFileIterator) Next() bool {
method Err (line 305) | func (itr *ltxFileIterator) Err() error { return itr.err }
method Item (line 307) | func (itr *ltxFileIterator) Item() *ltx.FileInfo {
function newLTXFileIterator (line 247) | func newLTXFileIterator(it *storage.ObjectIterator, client *ReplicaClien...
function isNotExists (line 311) | func isNotExists(err error) bool {
FILE: gs/replica_client_test.go
function ltxTestData (line 14) | func ltxTestData(tb testing.TB, minTXID, maxTXID ltx.TXID, payload []byt...
function setupTestClient (line 34) | func setupTestClient(tb testing.TB) (*ReplicaClient, *fakestorage.Server) {
function TestReplicaClient_OpenLTXFileReadsFullObject (line 56) | func TestReplicaClient_OpenLTXFileReadsFullObject(t *testing.T) {
FILE: heartbeat.go
constant DefaultHeartbeatInterval (line 12) | DefaultHeartbeatInterval = 5 * time.Minute
constant DefaultHeartbeatTimeout (line 13) | DefaultHeartbeatTimeout = 30 * time.Second
constant MinHeartbeatInterval (line 14) | MinHeartbeatInterval = 1 * time.Minute
type HeartbeatClient (line 17) | type HeartbeatClient struct
method Ping (line 45) | func (c *HeartbeatClient) Ping(ctx context.Context) error {
method ShouldPing (line 68) | func (c *HeartbeatClient) ShouldPing() bool {
method LastPingAt (line 74) | func (c *HeartbeatClient) LastPingAt() time.Time {
method RecordPing (line 80) | func (c *HeartbeatClient) RecordPing() {
function NewHeartbeatClient (line 28) | func NewHeartbeatClient(url string, interval time.Duration) *HeartbeatCl...
FILE: heartbeat_test.go
function TestHeartbeatClient_Ping (line 16) | func TestHeartbeatClient_Ping(t *testing.T) {
function TestHeartbeatClient_ShouldPing (line 84) | func TestHeartbeatClient_ShouldPing(t *testing.T) {
function TestHeartbeatClient_MinInterval (line 102) | func TestHeartbeatClient_MinInterval(t *testing.T) {
function TestHeartbeatClient_LastPingAt (line 109) | func TestHeartbeatClient_LastPingAt(t *testing.T) {
function TestStore_Heartbeat_AllDatabasesHealthy (line 126) | func TestStore_Heartbeat_AllDatabasesHealthy(t *testing.T) {
FILE: internal/hexdump.go
function Hexdump (line 8) | func Hexdump(data []byte) string {
function toChar (line 40) | func toChar(b byte) string {
FILE: internal/internal.go
constant LevelTrace (line 14) | LevelTrace = slog.LevelDebug - 4
type ReadCloser (line 17) | type ReadCloser struct
method Read (line 28) | func (r *ReadCloser) Read(p []byte) (n int, err error) {
method Close (line 33) | func (r *ReadCloser) Close() error {
function NewReadCloser (line 23) | func NewReadCloser(r io.Reader, c io.Closer) *ReadCloser {
type LZ4ReadCloser (line 44) | type LZ4ReadCloser struct
method Close (line 49) | func (r *LZ4ReadCloser) Close() error {
function NewLZ4Reader (line 55) | func NewLZ4Reader(r io.ReadCloser) io.ReadCloser {
type ReadCounter (line 63) | type ReadCounter struct
method Read (line 74) | func (r *ReadCounter) Read(p []byte) (int, error) {
method N (line 81) | func (r *ReadCounter) N() int64 { return r.n }
function NewReadCounter (line 69) | func NewReadCounter(r io.Reader) *ReadCounter {
function CreateFile (line 84) | func CreateFile(filename string, fi os.FileInfo) (*os.File, error) {
function MkdirAll (line 102) | func MkdirAll(path string, fi os.FileInfo) error {
function ReplaceAttr (line 153) | func ReplaceAttr(groups []string, a slog.Attr) slog.Attr {
FILE: internal/internal_unix.go
function Fileinfo (line 12) | func Fileinfo(fi os.FileInfo) (uid, gid int) {
function fixRootDirectory (line 23) | func fixRootDirectory(p string) string {
FILE: internal/internal_windows.go
function Fileinfo (line 11) | func Fileinfo(fi os.FileInfo) (uid, gid int) {
function fixRootDirectory (line 16) | func fixRootDirectory(p string) string {
FILE: internal/limit_read_closer.go
function LimitReadCloser (line 6) | func LimitReadCloser(r io.ReadCloser, n int64) io.ReadCloser {
type LimitedReadCloser (line 10) | type LimitedReadCloser struct
method Close (line 15) | func (l *LimitedReadCloser) Close() error {
method Read (line 19) | func (l *LimitedReadCloser) Read(p []byte) (n int, err error) {
FILE: internal/lock_unix.go
constant sqlitePendingByte (line 12) | sqlitePendingByte = 0x40000000
constant sqliteSharedFirst (line 13) | sqliteSharedFirst = sqlitePendingByte + 2
constant sqliteSharedSize (line 14) | sqliteSharedSize = 510
function LockFileExclusive (line 17) | func LockFileExclusive(f *os.File) error {
function UnlockFile (line 32) | func UnlockFile(f *os.File) error {
function setFcntlLock (line 42) | func setFcntlLock(fd int, lockType int16, start int64, length int64) err...
FILE: internal/lock_windows.go
constant sqlitePendingByte (line 12) | sqlitePendingByte = 0x40000000
constant sqliteSharedFirst (line 13) | sqliteSharedFirst = sqlitePendingByte + 2
constant sqliteSharedSize (line 14) | sqliteSharedSize = 510
function LockFileExclusive (line 17) | func LockFileExclusive(f *os.File) error {
function UnlockFile (line 34) | func UnlockFile(f *os.File) error {
FILE: internal/resumable_reader.go
type LTXFileOpener (line 12) | type LTXFileOpener interface
type ResumableReader (line 36) | type ResumableReader struct
method Read (line 64) | func (r *ResumableReader) Read(p []byte) (int, error) {
method Close (line 120) | func (r *ResumableReader) Close() error {
function NewResumableReader (line 49) | func NewResumableReader(ctx context.Context, client LTXFileOpener, level...
constant resumableReaderMaxRetries (line 62) | resumableReaderMaxRetries = 3
FILE: internal/resumable_reader_test.go
function TestResumableReader (line 15) | func TestResumableReader(t *testing.T) {
function newTestResumableReader (line 243) | func newTestResumableReader(client *testLTXFileOpener, size int64, data ...
type testLTXFileOpener (line 257) | type testLTXFileOpener struct
method OpenLTXFile (line 261) | func (t *testLTXFileOpener) OpenLTXFile(ctx context.Context, level int...
type errorAfterN (line 268) | type errorAfterN struct
method Read (line 275) | func (r *errorAfterN) Read(p []byte) (int, error) {
FILE: internal/testingutil/testingutil.go
constant defaultTigrisEndpoint (line 34) | defaultTigrisEndpoint = "https://fly.storage.tigris.dev"
constant defaultTigrisRegion (line 35) | defaultTigrisRegion = "auto"
constant defaultTigrisBucket (line 36) | defaultTigrisBucket = "litestream-dev"
constant defaultTigrisPathRoot (line 37) | defaultTigrisPathRoot = "integration-tests"
function Integration (line 135) | func Integration() bool {
function ReplicaClientTypes (line 139) | func ReplicaClientTypes() []string {
function NewDB (line 143) | func NewDB(tb testing.TB, path string) *litestream.DB {
function MustOpenDBs (line 161) | func MustOpenDBs(tb testing.TB) (*litestream.DB, *sql.DB) {
function MustCloseDBs (line 168) | func MustCloseDBs(tb testing.TB, db *litestream.DB, sqldb *sql.DB) {
function MustOpenDB (line 175) | func MustOpenDB(tb testing.TB) *litestream.DB {
function MustOpenDBAt (line 182) | func MustOpenDBAt(tb testing.TB, path string) *litestream.DB {
function MustCloseDB (line 197) | func MustCloseDB(tb testing.TB, db *litestream.DB) {
function MustOpenSQLDB (line 207) | func MustOpenSQLDB(tb testing.TB, path string) *sql.DB {
function MustCloseSQLDB (line 221) | func MustCloseSQLDB(tb testing.TB, d *sql.DB) {
function NewReplicaClient (line 229) | func NewReplicaClient(tb testing.TB, typ string) litestream.ReplicaClient {
function NewFileReplicaClient (line 262) | func NewFileReplicaClient(tb testing.TB) *file.ReplicaClient {
function NewS3ReplicaClient (line 268) | func NewS3ReplicaClient(tb testing.TB) *s3.ReplicaClient {
function NewTigrisReplicaClient (line 284) | func NewTigrisReplicaClient(tb testing.TB) *s3.ReplicaClient {
function NewR2ReplicaClient (line 305) | func NewR2ReplicaClient(tb testing.TB) *s3.ReplicaClient {
function NewB2ReplicaClient (line 332) | func NewB2ReplicaClient(tb testing.TB) *s3.ReplicaClient {
function NewGSReplicaClient (line 358) | func NewGSReplicaClient(tb testing.TB) *gs.ReplicaClient {
function NewABSReplicaClient (line 388) | func NewABSReplicaClient(tb testing.TB) *abs.ReplicaClient {
function NewSFTPReplicaClient (line 401) | func NewSFTPReplicaClient(tb testing.TB) *sftp.ReplicaClient {
function NewWebDAVReplicaClient (line 414) | func NewWebDAVReplicaClient(tb testing.TB) *webdav.ReplicaClient {
function NewNATSReplicaClient (line 426) | func NewNATSReplicaClient(tb testing.TB) *nats.ReplicaClient {
function NewOSSReplicaClient (line 439) | func NewOSSReplicaClient(tb testing.TB) *oss.ReplicaClient {
function MustDeleteAll (line 453) | func MustDeleteAll(tb testing.TB, c litestream.ReplicaClient) {
function MockSFTPServer (line 468) | func MockSFTPServer(t *testing.T, hostKey ssh.Signer) string {
FILE: leaser.go
type LeaseExistsError (line 12) | type LeaseExistsError struct
method Error (line 17) | func (e *LeaseExistsError) Error() string {
type Leaser (line 24) | type Leaser interface
type Lease (line 31) | type Lease struct
method IsExpired (line 38) | func (l *Lease) IsExpired() bool {
method TTL (line 42) | func (l *Lease) TTL() time.Duration {
FILE: litestream.go
constant MetaDirSuffix (line 20) | MetaDirSuffix = "-litestream"
constant CheckpointModePassive (line 25) | CheckpointModePassive = "PASSIVE"
constant CheckpointModeFull (line 26) | CheckpointModeFull = "FULL"
constant CheckpointModeRestart (line 27) | CheckpointModeRestart = "RESTART"
constant CheckpointModeTruncate (line 28) | CheckpointModeTruncate = "TRUNCATE"
type LTXError (line 40) | type LTXError struct
method Error (line 50) | func (e *LTXError) Error() string {
method Unwrap (line 57) | func (e *LTXError) Unwrap() error { return e.Err }
function NewLTXError (line 60) | func NewLTXError(op, path string, level int, minTXID, maxTXID uint64, er...
constant WALHeaderChecksumOffset (line 83) | WALHeaderChecksumOffset = 24
constant WALFrameHeaderChecksumOffset (line 84) | WALFrameHeaderChecksumOffset = 16
function Checksum (line 96) | func Checksum(bo binary.ByteOrder, s0, s1 uint32, b []byte) (uint32, uin...
constant WALHeaderSize (line 109) | WALHeaderSize = 32
constant WALFrameHeaderSize (line 112) | WALFrameHeaderSize = 24
function rollback (line 116) | func rollback(tx *sql.Tx) error {
function readWALHeader (line 124) | func readWALHeader(filename string) ([]byte, error) {
function readWALFileAt (line 138) | func readWALFileAt(filename string, offset, n int64) ([]byte, error) {
function removeTmpFiles (line 155) | func removeTmpFiles(root string) error {
function LTXDir (line 171) | func LTXDir(root string) string {
function LTXLevelDir (line 176) | func LTXLevelDir(root string, level int) string {
function LTXFilePath (line 181) | func LTXFilePath(root string, level int, minTXID, maxTXID ltx.TXID) stri...
function assert (line 185) | func assert(condition bool, message string) {
FILE: litestream_test.go
function TestChecksum (line 17) | func TestChecksum(t *testing.T) {
function TestLTXDir (line 47) | func TestLTXDir(t *testing.T) {
function TestLTXLevelDir (line 53) | func TestLTXLevelDir(t *testing.T) {
function LTXFilePath (line 59) | func LTXFilePath(t *testing.T) {
function MustDecodeHexString (line 66) | func MustDecodeHexString(s string) []byte {
function TestNewLTXError (line 74) | func TestNewLTXError(t *testing.T) {
function TestLTXErrorHints (line 125) | func TestLTXErrorHints(t *testing.T) {
FILE: log.go
constant LogKeySystem (line 4) | LogKeySystem = "system"
constant LogKeySubsystem (line 5) | LogKeySubsystem = "subsystem"
constant LogKeyDB (line 6) | LogKeyDB = "db"
constant LogSystemStore (line 10) | LogSystemStore = "store"
constant LogSystemServer (line 11) | LogSystemServer = "server"
constant LogSubsystemCompactor (line 15) | LogSubsystemCompactor = "compactor"
constant LogSubsystemWALReader (line 16) | LogSubsystemWALReader = "wal-reader"
FILE: mock/replica_client.go
type ReplicaClient (line 15) | type ReplicaClient struct
method Type (line 24) | func (c *ReplicaClient) Type() string { return "mock" }
method Init (line 26) | func (c *ReplicaClient) Init(ctx context.Context) error {
method DeleteAll (line 33) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method LTXFiles (line 37) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method OpenLTXFile (line 41) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method WriteLTXFile (line 45) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method DeleteLTXFiles (line 49) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method SetLogger (line 53) | func (c *ReplicaClient) SetLogger(_ *slog.Logger) {}
FILE: nats/replica_client.go
function init (line 26) | func init() {
constant ReplicaClientType (line 31) | ReplicaClientType = "nats"
constant HeaderKeyTimestamp (line 34) | HeaderKeyTimestamp = "Litestream-Timestamp"
type ReplicaClient (line 39) | type ReplicaClient struct
method SetLogger (line 92) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 124) | func (c *ReplicaClient) Type() string {
method Init (line 129) | func (c *ReplicaClient) Init(ctx context.Context) error {
method connect (line 149) | func (c *ReplicaClient) connect(_ context.Context) error {
method initObjectStore (line 213) | func (c *ReplicaClient) initObjectStore(ctx context.Context) error {
method Close (line 229) | func (c *ReplicaClient) Close() error {
method ltxPath (line 243) | func (c *ReplicaClient) ltxPath(level int, minTXID, maxTXID ltx.TXID) ...
method parseLTXPath (line 248) | func (c *ReplicaClient) parseLTXPath(objPath string) (level int, minTX...
method LTXFiles (line 278) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method OpenLTXFile (line 347) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method WriteLTXFile (line 383) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method DeleteLTXFiles (line 429) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method DeleteAll (line 451) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
function NewReplicaClient (line 80) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 99) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
function isNotFoundError (line 480) | func isNotFoundError(err error) bool {
type ltxFileIterator (line 485) | type ltxFileIterator struct
method Next (line 492) | func (itr *ltxFileIterator) Next() bool {
method Item (line 498) | func (itr *ltxFileIterator) Item() *ltx.FileInfo {
method Err (line 506) | func (itr *ltxFileIterator) Err() error {
method Close (line 511) | func (itr *ltxFileIterator) Close() error {
FILE: nats/replica_client_test.go
function TestReplicaClient_Type (line 10) | func TestReplicaClient_Type(t *testing.T) {
function TestReplicaClient_ltxPath (line 17) | func TestReplicaClient_ltxPath(t *testing.T) {
function TestReplicaClient_parseLTXPath (line 38) | func TestReplicaClient_parseLTXPath(t *testing.T) {
function TestReplicaClient_isNotFoundError (line 102) | func TestReplicaClient_isNotFoundError(t *testing.T) {
function TestLtxFileIterator (line 134) | func TestLtxFileIterator(t *testing.T) {
type mockNotFoundError (line 186) | type mockNotFoundError struct
method Error (line 188) | func (e *mockNotFoundError) Error() string {
type mockOtherError (line 192) | type mockOtherError struct
method Error (line 194) | func (e *mockOtherError) Error() string {
function TestReplicaClientDefaults (line 198) | func TestReplicaClientDefaults(t *testing.T) {
FILE: oss/replica_client.go
function init (line 28) | func init() {
constant ReplicaClientType (line 33) | ReplicaClientType = "oss"
constant MetadataKeyTimestamp (line 37) | MetadataKeyTimestamp = "litestream-timestamp"
constant MaxKeys (line 40) | MaxKeys = 1000
constant DefaultRegion (line 43) | DefaultRegion = "cn-hangzhou"
constant DefaultMetadataConcurrency (line 47) | DefaultMetadataConcurrency = 50
type ReplicaClient (line 52) | type ReplicaClient struct
method SetLogger (line 85) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 108) | func (c *ReplicaClient) Type() string {
method Init (line 113) | func (c *ReplicaClient) Init(ctx context.Context) (err error) {
method LTXFiles (line 185) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method OpenLTXFile (line 194) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method WriteLTXFile (line 233) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method DeleteLTXFiles (line 291) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method DeleteAll (line 336) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method ltxPath (line 389) | func (c *ReplicaClient) ltxPath(level int, filename string) string {
function NewReplicaClient (line 79) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 92) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
type fileIterator (line 394) | type fileIterator struct
method fetchMetadataBatch (line 431) | func (itr *fileIterator) fetchMetadataBatch(keys []string) error {
method initPaginator (line 494) | func (itr *fileIterator) initPaginator() {
method Close (line 509) | func (itr *fileIterator) Close() (err error) {
method Next (line 516) | func (itr *fileIterator) Next() bool {
method Item (line 614) | func (itr *fileIterator) Item() *ltx.FileInfo {
method Err (line 619) | func (itr *fileIterator) Err() error {
function newFileIterator (line 414) | func newFileIterator(ctx context.Context, client *ReplicaClient, level i...
function ParseURL (line 624) | func ParseURL(s string) (bucket, region, key string, err error) {
function ParseHost (line 649) | func ParseHost(host string) (bucket, region, endpoint string) {
function isNotExists (line 680) | func isNotExists(err error) bool {
function deleteResultError (line 691) | func deleteResultError(requested []oss.DeleteObject, out *oss.DeleteMult...
FILE: oss/replica_client_test.go
function TestReplicaClient_Type (line 10) | func TestReplicaClient_Type(t *testing.T) {
function TestReplicaClient_Init_BucketValidation (line 20) | func TestReplicaClient_Init_BucketValidation(t *testing.T) {
function TestReplicaClient_Init_Idempotent (line 63) | func TestReplicaClient_Init_Idempotent(t *testing.T) {
function TestParseURL (line 80) | func TestParseURL(t *testing.T) {
function TestParseHost (line 157) | func TestParseHost(t *testing.T) {
function TestIsNotExists (line 240) | func TestIsNotExists(t *testing.T) {
function TestLtxPath (line 262) | func TestLtxPath(t *testing.T) {
function TestDeleteResultError (line 286) | func TestDeleteResultError(t *testing.T) {
function contains (line 361) | func contains(s, substr string) bool {
function containsHelper (line 365) | func containsHelper(s, substr string) bool {
FILE: packages/npm/litestream-vfs/index.js
constant PLATFORM_PACKAGES (line 6) | const PLATFORM_PACKAGES = {
constant EXT_MAP (line 13) | const EXT_MAP = {
function getLoadablePath (line 18) | function getLoadablePath() {
FILE: packages/python/litestream_vfs/__init__.py
function loadable_path (line 12) | def loadable_path():
function load (line 26) | def load(conn):
FILE: packages/python/litestream_vfs/noop.c
type PyModuleDef (line 6) | struct PyModuleDef
function PyMODINIT_FUNC (line 10) | PyMODINIT_FUNC PyInit__noop(void) { return PyModule_Create(&module); }
FILE: packages/python/scripts/rename_wheel.py
function main (line 13) | def main():
FILE: packages/ruby/lib/litestream_vfs.rb
type LitestreamVfs (line 3) | module LitestreamVfs
function loadable_path (line 9) | def self.loadable_path
function load (line 23) | def self.load(db)
FILE: replica.go
constant DefaultSyncInterval (line 24) | DefaultSyncInterval = 1 * time.Second
type Replica (line 30) | type Replica struct
method Logger (line 80) | func (r *Replica) Logger() *slog.Logger {
method DB (line 89) | func (r *Replica) DB() *DB { return r.db }
method Start (line 92) | func (r *Replica) Start(ctx context.Context) error {
method Stop (line 116) | func (r *Replica) Stop(hard bool) (err error) {
method Sync (line 132) | func (r *Replica) Sync(ctx context.Context) (err error) {
method uploadLTXFile (line 182) | func (r *Replica) uploadLTXFile(ctx context.Context, level int, minTXI...
method calcPos (line 208) | func (r *Replica) calcPos(ctx context.Context) (pos ltx.Pos, err error) {
method MaxLTXFileInfo (line 218) | func (r *Replica) MaxLTXFileInfo(ctx context.Context, level int) (info...
method Pos (line 237) | func (r *Replica) Pos() ltx.Pos {
method SetPos (line 244) | func (r *Replica) SetPos(pos ltx.Pos) {
method EnforceRetention (line 252) | func (r *Replica) EnforceRetention(ctx context.Context) (err error) {
method monitor (line 326) | func (r *Replica) monitor(ctx context.Context) {
method CreatedAt (line 443) | func (r *Replica) CreatedAt(ctx context.Context) (time.Time, error) {
method TimeBounds (line 461) | func (r *Replica) TimeBounds(ctx context.Context) (createdAt, updatedA...
method CalcRestoreTarget (line 485) | func (r *Replica) CalcRestoreTarget(ctx context.Context, opt RestoreOp...
method Restore (line 531) | func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (er...
method follow (line 728) | func (r *Replica) follow(ctx context.Context, outputPath string, lastT...
method applyNewLTXFiles (line 789) | func (r *Replica) applyNewLTXFiles(ctx context.Context, f *os.File, af...
method applyLTXFile (line 870) | func (r *Replica) applyLTXFile(ctx context.Context, f *os.File, info *...
method fillFollowGap (line 925) | func (r *Replica) fillFollowGap(ctx context.Context, f *os.File, after...
method RestoreV3 (line 988) | func (r *Replica) RestoreV3(ctx context.Context, opt RestoreOptions) e...
method downloadSnapshotV3 (line 1142) | func (r *Replica) downloadSnapshotV3(ctx context.Context, client Repli...
method applyWALSegmentsV3 (line 1162) | func (r *Replica) applyWALSegmentsV3(ctx context.Context, client Repli...
method writeWALSegmentV3 (line 1180) | func (r *Replica) writeWALSegmentV3(ctx context.Context, client Replic...
method findBestV3SnapshotForTimestamp (line 1256) | func (r *Replica) findBestV3SnapshotForTimestamp(ctx context.Context, ...
method shouldUseV3Restore (line 1286) | func (r *Replica) shouldUseV3Restore(ctx context.Context, client Repli...
method TimeBoundsV3 (line 1344) | func (r *Replica) TimeBoundsV3(ctx context.Context, client ReplicaClie...
method findBestLTXSnapshotForTimestamp (line 1383) | func (r *Replica) findBestLTXSnapshotForTimestamp(ctx context.Context,...
method ValidateLevel (line 1675) | func (r *Replica) ValidateLevel(ctx context.Context, level int) ([]Val...
function NewReplica (line 61) | func NewReplica(db *DB) *Replica {
function NewReplicaWithClient (line 73) | func NewReplicaWithClient(db *DB, client ReplicaClient) *Replica {
function sortSnapshotsV3ByCreatedAt (line 1097) | func sortSnapshotsV3ByCreatedAt(snapshots []SnapshotInfoV3) {
function findBestSnapshotV3 (line 1110) | func findBestSnapshotV3(snapshots []SnapshotInfoV3, timestamp time.Time)...
function filterWALSegmentsV3 (line 1127) | func filterWALSegmentsV3(segments []WALSegmentInfoV3, snapshotIndex int,...
function checkpointV3 (line 1206) | func checkpointV3(dbPath string) error {
function checkIntegrity (line 1218) | func checkIntegrity(ctx context.Context, dbPath string, mode IntegrityCh...
function CalcRestorePlan (line 1400) | func CalcRestorePlan(ctx context.Context, client ReplicaClient, txID ltx...
type restoreLevelCursor (line 1518) | type restoreLevelCursor struct
method refresh (line 1529) | func (c *restoreLevelCursor) refresh(currentMax, txID ltx.TXID, timest...
method ensureCurrent (line 1569) | func (c *restoreLevelCursor) ensureCurrent() error {
function restoreCandidateBetter (line 1585) | func restoreCandidateBetter(curr, next *ltx.FileInfo) bool {
function TXIDPath (line 1600) | func TXIDPath(outputPath string) string {
function WriteTXIDFile (line 1606) | func WriteTXIDFile(outputPath string, txid ltx.TXID) error {
function ReadTXIDFile (line 1646) | func ReadTXIDFile(outputPath string) (ltx.TXID, error) {
type ValidationError (line 1665) | type ValidationError struct
FILE: replica_client.go
type ReplicaClient (line 19) | type ReplicaClient interface
function FindLTXFiles (line 57) | func FindLTXFiles(ctx context.Context, client ReplicaClient, level int, ...
constant DefaultEstimatedPageIndexSize (line 88) | DefaultEstimatedPageIndexSize = 32 * 1024
function FetchPageIndex (line 90) | func FetchPageIndex(ctx context.Context, client ReplicaClient, info *ltx...
function FetchLTXHeader (line 101) | func FetchLTXHeader(ctx context.Context, client ReplicaClient, info *ltx...
function fetchPageIndexData (line 116) | func fetchPageIndexData(ctx context.Context, client ReplicaClient, info ...
function FetchPage (line 148) | func FetchPage(ctx context.Context, client ReplicaClient, level int, min...
FILE: replica_client_test.go
function createLTXData (line 27) | func createLTXData(minTXID, maxTXID ltx.TXID, data []byte) []byte {
function createLTXDataWithTimestamp (line 31) | func createLTXDataWithTimestamp(minTXID, maxTXID ltx.TXID, ts time.Time,...
function TestReplicaClient_LTX (line 51) | func TestReplicaClient_LTX(t *testing.T) {
function TestReplicaClient_WriteLTXFile (line 131) | func TestReplicaClient_WriteLTXFile(t *testing.T) {
function TestReplicaClient_OpenLTXFile (line 165) | func TestReplicaClient_OpenLTXFile(t *testing.T) {
function TestReplicaClient_DeleteWALSegments (line 201) | func TestReplicaClient_DeleteWALSegments(t *testing.T) {
function RunWithReplicaClient (line 230) | func RunWithReplicaClient(t *testing.T, name string, fn func(*testing.T,...
function TestReplicaClient_TimestampPreservation (line 249) | func TestReplicaClient_TimestampPreservation(t *testing.T) {
function TestReplicaClient_S3_UploaderConfig (line 307) | func TestReplicaClient_S3_UploaderConfig(t *testing.T) {
function TestReplicaClient_S3_ErrorContext (line 375) | func TestReplicaClient_S3_ErrorContext(t *testing.T) {
function TestReplicaClient_S3_BucketValidation (line 399) | func TestReplicaClient_S3_BucketValidation(t *testing.T) {
function TestReplicaClient_S3_UnsignedPayloadRejected (line 426) | func TestReplicaClient_S3_UnsignedPayloadRejected(t *testing.T) {
function TestReplicaClient_SFTP_HostKeyValidation (line 473) | func TestReplicaClient_SFTP_HostKeyValidation(t *testing.T) {
function TestReplicaClient_S3_MultipartThresholds (line 555) | func TestReplicaClient_S3_MultipartThresholds(t *testing.T) {
function TestReplicaClient_S3_ConcurrencyLimits (line 644) | func TestReplicaClient_S3_ConcurrencyLimits(t *testing.T) {
function TestReplicaClient_PITR_ManyLTXFiles (line 709) | func TestReplicaClient_PITR_ManyLTXFiles(t *testing.T) {
function TestReplicaClient_PITR_TimestampFiltering (line 807) | func TestReplicaClient_PITR_TimestampFiltering(t *testing.T) {
function TestReplicaClient_PITR_CalcRestorePlanWithManyFiles (line 874) | func TestReplicaClient_PITR_CalcRestorePlanWithManyFiles(t *testing.T) {
FILE: replica_internal_test.go
function TestReplica_ApplyNewLTXFiles_FillGapWithOverlappingCompactedFile (line 20) | func TestReplica_ApplyNewLTXFiles_FillGapWithOverlappingCompactedFile(t ...
function TestReplica_ApplyNewLTXFiles_LevelZeroEmptyFallsBackToCompaction (line 73) | func TestReplica_ApplyNewLTXFiles_LevelZeroEmptyFallsBackToCompaction(t ...
function TestReplica_ApplyNewLTXFiles_IteratorCloseError (line 117) | func TestReplica_ApplyNewLTXFiles_IteratorCloseError(t *testing.T) {
function TestReplica_ApplyLTXFile_VerifiesChecksumOnClose (line 142) | func TestReplica_ApplyLTXFile_VerifiesChecksumOnClose(t *testing.T) {
type errorFileIterator (line 167) | type errorFileIterator struct
method Close (line 220) | func (itr *errorFileIterator) Close() error {
method Next (line 224) | func (itr *errorFileIterator) Next() bool {
method Err (line 228) | func (itr *errorFileIterator) Err() error {
method Item (line 232) | func (itr *errorFileIterator) Item() *ltx.FileInfo {
type followTestReplicaClient (line 171) | type followTestReplicaClient struct
method Type (line 179) | func (*followTestReplicaClient) Type() string { return "test" }
method Init (line 181) | func (*followTestReplicaClient) Init(context.Context) error { return n...
method SetLogger (line 183) | func (*followTestReplicaClient) SetLogger(*slog.Logger) {}
method LTXFiles (line 185) | func (c *followTestReplicaClient) LTXFiles(ctx context.Context, level ...
method OpenLTXFile (line 192) | func (c *followTestReplicaClient) OpenLTXFile(ctx context.Context, lev...
method WriteLTXFile (line 199) | func (c *followTestReplicaClient) WriteLTXFile(ctx context.Context, le...
method DeleteLTXFiles (line 206) | func (c *followTestReplicaClient) DeleteLTXFiles(ctx context.Context, ...
method DeleteAll (line 213) | func (c *followTestReplicaClient) DeleteAll(ctx context.Context) error {
function mustBuildIncrementalLTX (line 236) | func mustBuildIncrementalLTX(tb testing.TB, minTXID, maxTXID ltx.TXID, p...
function mustCreateWritableDBFile (line 268) | func mustCreateWritableDBFile(tb testing.TB) *os.File {
function ltxFixtureKey (line 283) | func ltxFixtureKey(level int, minTXID, maxTXID ltx.TXID) string {
function mustCreateValidSQLiteDB (line 287) | func mustCreateValidSQLiteDB(tb testing.TB) string {
function TestCheckIntegrity_Quick_ValidDB (line 307) | func TestCheckIntegrity_Quick_ValidDB(t *testing.T) {
function TestCheckIntegrity_Full_ValidDB (line 314) | func TestCheckIntegrity_Full_ValidDB(t *testing.T) {
function TestCheckIntegrity_None_Skips (line 321) | func TestCheckIntegrity_None_Skips(t *testing.T) {
function TestCheckIntegrity_CorruptDB (line 327) | func TestCheckIntegrity_CorruptDB(t *testing.T) {
FILE: replica_test.go
function TestReplica_Sync (line 25) | func TestReplica_Sync(t *testing.T) {
function TestReplica_RestoreAndReplicateAfterDataLoss (line 111) | func TestReplica_RestoreAndReplicateAfterDataLoss(t *testing.T) {
function TestReplica_CalcRestorePlan (line 257) | func TestReplica_CalcRestorePlan(t *testing.T) {
function TestReplica_TimeBounds (line 533) | func TestReplica_TimeBounds(t *testing.T) {
function TestReplica_CalcRestoreTarget (line 695) | func TestReplica_CalcRestoreTarget(t *testing.T) {
function TestReplica_Restore_InvalidFileSize (line 797) | func TestReplica_Restore_InvalidFileSize(t *testing.T) {
function TestReplica_ContextCancellationNoLogs (line 860) | func TestReplica_ContextCancellationNoLogs(t *testing.T) {
function TestReplica_ValidateLevel (line 943) | func TestReplica_ValidateLevel(t *testing.T) {
function TestReplica_RestoreV3 (line 1057) | func TestReplica_RestoreV3(t *testing.T) {
function TestReplica_Restore_BothFormats (line 1299) | func TestReplica_Restore_BothFormats(t *testing.T) {
function createTestLTXSnapshot (line 1550) | func createTestLTXSnapshot(t *testing.T) []byte {
type v3SnapshotData (line 1603) | type v3SnapshotData struct
type v3WALSegmentData (line 1609) | type v3WALSegmentData struct
function createV3Backup (line 1616) | func createV3Backup(t *testing.T, replicaDir, generation string, snapsho...
function writeV3Snapshot (line 1643) | func writeV3Snapshot(t *testing.T, dir string, index int, data []byte) {
function writeV3WALSegment (line 1662) | func writeV3WALSegment(t *testing.T, dir string, index int, offset int64...
function createTestSQLiteDB (line 1681) | func createTestSQLiteDB(t *testing.T) []byte {
function createTestWALData (line 1703) | func createTestWALData(t *testing.T, dbData []byte) []byte {
function TestWriteTXIDFile (line 1722) | func TestWriteTXIDFile(t *testing.T) {
function TestReadTXIDFile (line 1764) | func TestReadTXIDFile(t *testing.T) {
function TestReplica_Restore_Follow_IncompatibleFlags (line 1824) | func TestReplica_Restore_Follow_IncompatibleFlags(t *testing.T) {
function TestReplica_Restore_Follow (line 1854) | func TestReplica_Restore_Follow(t *testing.T) {
function TestReplica_Restore_Follow_ContextCancellation (line 1957) | func TestReplica_Restore_Follow_ContextCancellation(t *testing.T) {
function TestReplica_Restore_Follow_WriteTXIDFile (line 2015) | func TestReplica_Restore_Follow_WriteTXIDFile(t *testing.T) {
function TestReplica_Restore_Follow_CrashRecovery (line 2125) | func TestReplica_Restore_Follow_CrashRecovery(t *testing.T) {
function TestReplica_Restore_Follow_NoTXIDFile (line 2256) | func TestReplica_Restore_Follow_NoTXIDFile(t *testing.T) {
function TestReplica_Restore_Follow_StaleTXID (line 2282) | func TestReplica_Restore_Follow_StaleTXID(t *testing.T) {
function verifyRestoredDB (line 2355) | func verifyRestoredDB(t *testing.T, path string) {
FILE: replica_url.go
type ReplicaClientFactory (line 14) | type ReplicaClientFactory
function RegisterReplicaClientFactory (line 23) | func RegisterReplicaClientFactory(scheme string, factory ReplicaClientFa...
function NewReplicaClientFromURL (line 31) | func NewReplicaClientFromURL(rawURL string) (ReplicaClient, error) {
function ReplicaTypeFromURL (line 56) | func ReplicaTypeFromURL(rawURL string) string {
function ParseReplicaURL (line 68) | func ParseReplicaURL(s string) (scheme, host, urlPath string, err error) {
function ParseReplicaURLWithQuery (line 79) | func ParseReplicaURLWithQuery(s string) (scheme, host, urlPath string, q...
function parseS3AccessPointURL (line 107) | func parseS3AccessPointURL(s string) (scheme, host, urlPath string, quer...
function splitS3AccessPointARN (line 139) | func splitS3AccessPointARN(s string) (bucket, key string, err error) {
function CleanReplicaURLPath (line 165) | func CleanReplicaURLPath(p string) string {
function RegionFromS3ARN (line 178) | func RegionFromS3ARN(arn string) string {
function BoolQueryValue (line 188) | func BoolQueryValue(query url.Values, keys ...string) (value bool, ok bo...
function IsHetznerEndpoint (line 208) | func IsHetznerEndpoint(endpoint string) bool {
function IsTigrisEndpoint (line 217) | func IsTigrisEndpoint(endpoint string) bool {
function IsDigitalOceanEndpoint (line 223) | func IsDigitalOceanEndpoint(endpoint string) bool {
function IsBackblazeEndpoint (line 232) | func IsBackblazeEndpoint(endpoint string) bool {
function IsFilebaseEndpoint (line 241) | func IsFilebaseEndpoint(endpoint string) bool {
function IsScalewayEndpoint (line 250) | func IsScalewayEndpoint(endpoint string) bool {
function IsCloudflareR2Endpoint (line 259) | func IsCloudflareR2Endpoint(endpoint string) bool {
function IsSupabaseEndpoint (line 268) | func IsSupabaseEndpoint(endpoint string) bool {
function IsMinIOEndpoint (line 278) | func IsMinIOEndpoint(endpoint string) bool {
function IsLocalEndpoint (line 306) | func IsLocalEndpoint(endpoint string) bool {
function EnsureEndpointScheme (line 335) | func EnsureEndpointScheme(endpoint string) (string, bool) {
function extractEndpointHost (line 351) | func extractEndpointHost(endpoint string) string {
function IsURL (line 367) | func IsURL(s string) bool {
FILE: replica_url_test.go
function TestNewReplicaClientFromURL (line 17) | func TestNewReplicaClientFromURL(t *testing.T) {
function TestReplicaTypeFromURL (line 456) | func TestReplicaTypeFromURL(t *testing.T) {
function TestIsURL (line 484) | func TestIsURL(t *testing.T) {
function TestBoolQueryValue (line 507) | func TestBoolQueryValue(t *testing.T) {
function TestIsTigrisEndpoint (line 576) | func TestIsTigrisEndpoint(t *testing.T) {
function TestRegionFromS3ARN (line 606) | func TestRegionFromS3ARN(t *testing.T) {
function TestCleanReplicaURLPath (line 630) | func TestCleanReplicaURLPath(t *testing.T) {
function TestParseS3AccessPointURL (line 658) | func TestParseS3AccessPointURL(t *testing.T) {
function TestIsDigitalOceanEndpoint (line 765) | func TestIsDigitalOceanEndpoint(t *testing.T) {
function TestIsBackblazeEndpoint (line 787) | func TestIsBackblazeEndpoint(t *testing.T) {
function TestIsFilebaseEndpoint (line 809) | func TestIsFilebaseEndpoint(t *testing.T) {
function TestIsScalewayEndpoint (line 831) | func TestIsScalewayEndpoint(t *testing.T) {
function TestIsCloudflareR2Endpoint (line 853) | func TestIsCloudflareR2Endpoint(t *testing.T) {
function TestIsSupabaseEndpoint (line 875) | func TestIsSupabaseEndpoint(t *testing.T) {
function TestIsHetznerEndpoint (line 897) | func TestIsHetznerEndpoint(t *testing.T) {
function TestIsMinIOEndpoint (line 921) | func TestIsMinIOEndpoint(t *testing.T) {
function TestIsLocalEndpoint (line 945) | func TestIsLocalEndpoint(t *testing.T) {
function TestS3ProviderDefaults (line 1003) | func TestS3ProviderDefaults(t *testing.T) {
function TestEnsureEndpointScheme (line 1101) | func TestEnsureEndpointScheme(t *testing.T) {
function TestS3ProviderDefaults_QueryParamOverrides (line 1145) | func TestS3ProviderDefaults_QueryParamOverrides(t *testing.T) {
FILE: restore_fuzz_test.go
function FuzzRestoreWithMissingCompactedFile (line 17) | func FuzzRestoreWithMissingCompactedFile(f *testing.F) {
FILE: s3/leaser.go
constant DefaultLeaseTTL (line 22) | DefaultLeaseTTL = 30 * time.Second
constant DefaultLeasePath (line 23) | DefaultLeasePath = "lock.json"
constant LeaserType (line 24) | LeaserType = "s3"
type S3API (line 36) | type S3API interface
type Leaser (line 42) | type Leaser struct
method SetLogger (line 67) | func (l *Leaser) SetLogger(logger *slog.Logger) {
method Client (line 71) | func (l *Leaser) Client() S3API {
method SetClient (line 75) | func (l *Leaser) SetClient(client S3API) {
method Type (line 79) | func (l *Leaser) Type() string {
method lockKey (line 83) | func (l *Leaser) lockKey() string {
method AcquireLease (line 90) | func (l *Leaser) AcquireLease(ctx context.Context) (*litestream.Lease,...
method RenewLease (line 138) | func (l *Leaser) RenewLease(ctx context.Context, lease *litestream.Lea...
method ReleaseLease (line 171) | func (l *Leaser) ReleaseLease(ctx context.Context, lease *litestream.L...
method readLease (line 202) | func (l *Leaser) readLease(ctx context.Context) (*litestream.Lease, st...
method writeLease (line 231) | func (l *Leaser) writeLease(ctx context.Context, lease *litestream.Lea...
function NewLeaser (line 52) | func NewLeaser() *Leaser {
function isPreconditionFailed (line 268) | func isPreconditionFailed(err error) bool {
function isNotFoundError (line 285) | func isNotFoundError(err error) bool {
FILE: s3/leaser_test.go
function TestLeaser_AcquireLease_NewLease (line 19) | func TestLeaser_AcquireLease_NewLease(t *testing.T) {
function TestLeaser_AcquireLease_ExpiredLease (line 78) | func TestLeaser_AcquireLease_ExpiredLease(t *testing.T) {
function TestLeaser_AcquireLease_ActiveLease (line 124) | func TestLeaser_AcquireLease_ActiveLease(t *testing.T) {
function TestLeaser_AcquireLease_RaceCondition412 (line 167) | func TestLeaser_AcquireLease_RaceCondition412(t *testing.T) {
function TestLeaser_RenewLease (line 213) | func TestLeaser_RenewLease(t *testing.T) {
function TestLeaser_RenewLease_LostLease (line 270) | func TestLeaser_RenewLease_LostLease(t *testing.T) {
function TestLeaser_RenewLease_NilLease (line 294) | func TestLeaser_RenewLease_NilLease(t *testing.T) {
function TestLeaser_RenewLease_EmptyETag (line 309) | func TestLeaser_RenewLease_EmptyETag(t *testing.T) {
function TestLeaser_ReleaseLease (line 330) | func TestLeaser_ReleaseLease(t *testing.T) {
function TestLeaser_ReleaseLease_StaleETag (line 366) | func TestLeaser_ReleaseLease_StaleETag(t *testing.T) {
function TestLeaser_ReleaseLease_AlreadyDeleted (line 390) | func TestLeaser_ReleaseLease_AlreadyDeleted(t *testing.T) {
function TestLeaser_ReleaseLease_NilLease (line 414) | func TestLeaser_ReleaseLease_NilLease(t *testing.T) {
function TestLeaser_ReleaseLease_EmptyETag (line 429) | func TestLeaser_ReleaseLease_EmptyETag(t *testing.T) {
function TestLeaser_ConcurrentAcquisition (line 450) | func TestLeaser_ConcurrentAcquisition(t *testing.T) {
function TestLeaser_LockKey (line 536) | func TestLeaser_LockKey(t *testing.T) {
function TestLeaser_Type (line 571) | func TestLeaser_Type(t *testing.T) {
function newTestLeaser (line 579) | func newTestLeaser(t *testing.T, serverURL string) *Leaser {
FILE: s3/replica_client.go
function init (line 45) | func init() {
constant ReplicaClientType (line 50) | ReplicaClientType = "s3"
constant MetadataKeyTimestamp (line 53) | MetadataKeyTimestamp = "litestream-timestamp"
constant MaxKeys (line 56) | MaxKeys = 1000
constant DefaultRegion (line 59) | DefaultRegion = "us-east-1"
constant DefaultMetadataConcurrency (line 64) | DefaultMetadataConcurrency = 50
constant DefaultR2Concurrency (line 68) | DefaultR2Concurrency = 2
type contentMD5StackKey (line 72) | type contentMD5StackKey struct
type ReplicaClient (line 78) | type ReplicaClient struct
method SetLogger (line 127) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 317) | func (c *ReplicaClient) Type() string {
method Init (line 322) | func (c *ReplicaClient) Init(ctx context.Context) (err error) {
method configureEndpoint (line 480) | func (c *ReplicaClient) configureEndpoint(opts *[]func(*s3.Options)) {
method validateSSEConfig (line 501) | func (c *ReplicaClient) validateSSEConfig() error {
method findBucketRegion (line 558) | func (c *ReplicaClient) findBucketRegion(ctx context.Context, bucket s...
method LTXFiles (line 606) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method OpenLTXFile (line 615) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method WriteLTXFile (line 657) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method middlewareOption (line 729) | func (c *ReplicaClient) middlewareOption() func(*middleware.Stack) err...
method DeleteLTXFiles (line 1028) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method DeleteAll (line 1104) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method GenerationsV3 (line 1151) | func (c *ReplicaClient) GenerationsV3(ctx context.Context) ([]string, ...
method SnapshotsV3 (line 1188) | func (c *ReplicaClient) SnapshotsV3(ctx context.Context, generation st...
method WALSegmentsV3 (line 1230) | func (c *ReplicaClient) WALSegmentsV3(ctx context.Context, generation ...
method OpenSnapshotV3 (line 1277) | func (c *ReplicaClient) OpenSnapshotV3(ctx context.Context, generation...
method OpenWALSegmentV3 (line 1309) | func (c *ReplicaClient) OpenWALSegmentV3(ctx context.Context, generati...
function NewReplicaClient (line 119) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 133) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
function computeDeleteObjectsContentMD5 (line 899) | func computeDeleteObjectsContentMD5(deleteInput *types.Delete) (string, ...
function marshalDeleteObjects (line 916) | func marshalDeleteObjects(deleteInput *types.Delete) ([]byte, error) {
function encodeDeleteDocument (line 939) | func encodeDeleteDocument(v *types.Delete, value smithyxml.Value) error {
function encodeObjectIdentifierList (line 964) | func encodeObjectIdentifierList(v []types.ObjectIdentifier, value smithy...
function encodeObjectIdentifier (line 982) | func encodeObjectIdentifier(v *types.ObjectIdentifier, value smithyxml.V...
type fileIterator (line 1340) | type fileIterator struct
method fetchMetadataBatch (line 1385) | func (itr *fileIterator) fetchMetadataBatch(keys []string) error {
method Close (line 1458) | func (itr *fileIterator) Close() (err error) {
method Next (line 1465) | func (itr *fileIterator) Next() bool {
method Item (line 1562) | func (itr *fileIterator) Item() *ltx.FileInfo {
method Err (line 1567) | func (itr *fileIterator) Err() error {
function newFileIterator (line 1360) | func newFileIterator(ctx context.Context, client *ReplicaClient, level i...
function ParseURL (line 1573) | func ParseURL(s, endpoint string) (bucket, region, key string, err error) {
function ParseHost (line 1608) | func ParseHost(host string) (bucket, region, endpoint string, forcePathS...
function isNotExists (line 1662) | func isNotExists(err error) bool {
function deleteOutputError (line 1670) | func deleteOutputError(out *s3.DeleteObjectsOutput) error {
function parseS3DebugEnv (line 1686) | func parseS3DebugEnv() aws.ClientLogMode {
FILE: s3/replica_client_test.go
type mockAPIError (line 32) | type mockAPIError struct
method Error (line 37) | func (e *mockAPIError) Error() string {
method ErrorCode (line 41) | func (e *mockAPIError) ErrorCode() string {
method ErrorMessage (line 45) | func (e *mockAPIError) ErrorMessage() string {
method ErrorFault (line 49) | func (e *mockAPIError) ErrorFault() smithy.ErrorFault {
function TestIsNotExists (line 53) | func TestIsNotExists(t *testing.T) {
function TestReplicaClient_DefaultSignPayload (line 93) | func TestReplicaClient_DefaultSignPayload(t *testing.T) {
function TestReplicaClientPayloadSigning (line 103) | func TestReplicaClientPayloadSigning(t *testing.T) {
function TestReplicaClient_UnsignedPayload_NoChunkedEncoding (line 169) | func TestReplicaClient_UnsignedPayload_NoChunkedEncoding(t *testing.T) {
function TestReplicaClient_SignedPayload_CustomEndpoint_NoChunkedEncoding (line 239) | func TestReplicaClient_SignedPayload_CustomEndpoint_NoChunkedEncoding(t ...
function mustLTX (line 311) | func mustLTX(t *testing.T) []byte {
function mustLTXWithSize (line 340) | func mustLTXWithSize(t *testing.T, size int) []byte {
function TestReplicaClient_MultipartUploadThreshold (line 351) | func TestReplicaClient_MultipartUploadThreshold(t *testing.T) {
function TestReplicaClient_Init_BucketValidation (line 468) | func TestReplicaClient_Init_BucketValidation(t *testing.T) {
function TestReplicaClient_UploaderConfiguration (line 497) | func TestReplicaClient_UploaderConfiguration(t *testing.T) {
function TestReplicaClient_ConfigureEndpoint (line 527) | func TestReplicaClient_ConfigureEndpoint(t *testing.T) {
function TestReplicaClient_HTTPClientConfiguration (line 582) | func TestReplicaClient_HTTPClientConfiguration(t *testing.T) {
function TestReplicaClientDeleteLTXFiles_ContentMD5 (line 608) | func TestReplicaClientDeleteLTXFiles_ContentMD5(t *testing.T) {
function TestReplicaClientDeleteLTXFiles_PreexistingContentMD5 (line 719) | func TestReplicaClientDeleteLTXFiles_PreexistingContentMD5(t *testing.T) {
function TestReplicaClient_CredentialConfiguration (line 785) | func TestReplicaClient_CredentialConfiguration(t *testing.T) {
function TestReplicaClient_DefaultRegionUsage (line 813) | func TestReplicaClient_DefaultRegionUsage(t *testing.T) {
function TestMarshalDeleteObjects_EdgeCases (line 837) | func TestMarshalDeleteObjects_EdgeCases(t *testing.T) {
function TestEncodeObjectIdentifier_AllFields (line 947) | func TestEncodeObjectIdentifier_AllFields(t *testing.T) {
function TestComputeDeleteObjectsContentMD5_Deterministic (line 1041) | func TestComputeDeleteObjectsContentMD5_Deterministic(t *testing.T) {
function TestParseHost (line 1071) | func TestParseHost(t *testing.T) {
function TestReplicaClient_AccessPointARN (line 1150) | func TestReplicaClient_AccessPointARN(t *testing.T) {
function TestReplicaClient_S3DebugEnvVar (line 1212) | func TestReplicaClient_S3DebugEnvVar(t *testing.T) {
function TestReplicaClient_TigrisConsistentHeader (line 1301) | func TestReplicaClient_TigrisConsistentHeader(t *testing.T) {
function TestReplicaClient_SSE_C_Validation (line 1355) | func TestReplicaClient_SSE_C_Validation(t *testing.T) {
function TestReplicaClient_SSE_KMS_Configuration (line 1533) | func TestReplicaClient_SSE_KMS_Configuration(t *testing.T) {
function TestReplicaClient_SSE_C_Headers (line 1560) | func TestReplicaClient_SSE_C_Headers(t *testing.T) {
function TestReplicaClient_SSE_KMS_Headers (line 1679) | func TestReplicaClient_SSE_KMS_Headers(t *testing.T) {
function TestReplicaClient_NoSSE_Headers (line 1735) | func TestReplicaClient_NoSSE_Headers(t *testing.T) {
function TestReplicaClient_R2ConcurrencyDefault (line 1795) | func TestReplicaClient_R2ConcurrencyDefault(t *testing.T) {
function TestReplicaClient_ProviderEndpointDetection (line 1835) | func TestReplicaClient_ProviderEndpointDetection(t *testing.T) {
function TestReplicaClient_CustomEndpoint_DisablesChecksumFeatures (line 1892) | func TestReplicaClient_CustomEndpoint_DisablesChecksumFeatures(t *testin...
function TestNewReplicaClientFromURL_QueryParamAliases (line 1940) | func TestNewReplicaClientFromURL_QueryParamAliases(t *testing.T) {
function TestNewReplicaClientFromURL_EndpointEnvVar (line 2023) | func TestNewReplicaClientFromURL_EndpointEnvVar(t *testing.T) {
FILE: server.go
type SocketConfig (line 18) | type SocketConfig struct
function DefaultSocketConfig (line 25) | func DefaultSocketConfig() SocketConfig {
type Server (line 34) | type Server struct
method Start (line 97) | func (s *Server) Start() error {
method Close (line 144) | func (s *Server) Close() error {
method expandPath (line 160) | func (s *Server) expandPath(path string) (string, error) {
method handleStart (line 167) | func (s *Server) handleStart(w http.ResponseWriter, r *http.Request) {
method handleStop (line 203) | func (s *Server) handleStop(w http.ResponseWriter, r *http.Request) {
method handleTXID (line 239) | func (s *Server) handleTXID(w http.ResponseWriter, r *http.Request) {
method handleSync (line 319) | func (s *Server) handleSync(w http.ResponseWriter, r *http.Request) {
method handleList (line 392) | func (s *Server) handleList(w http.ResponseWriter, _ *http.Request) {
method handleInfo (line 425) | func (s *Server) handleInfo(w http.ResponseWriter, _ *http.Request) {
method handleRegister (line 486) | func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
method handleUnregister (line 545) | func (s *Server) handleUnregister(w http.ResponseWriter, r *http.Reque...
function NewServer (line 64) | func NewServer(store *Store) *Server {
function writeJSON (line 269) | func writeJSON(w http.ResponseWriter, status int, v interface{}) {
function writeJSONError (line 275) | func writeJSONError(w http.ResponseWriter, status int, message string, d...
type StartRequest (line 285) | type StartRequest struct
type StartResponse (line 291) | type StartResponse struct
type StopRequest (line 297) | type StopRequest struct
type StopResponse (line 303) | type StopResponse struct
type ErrorResponse (line 309) | type ErrorResponse struct
type TXIDResponse (line 315) | type TXIDResponse struct
type SyncRequest (line 378) | type SyncRequest struct
type SyncResponse (line 385) | type SyncResponse struct
type ListResponse (line 438) | type ListResponse struct
type DatabaseSummary (line 443) | type DatabaseSummary struct
type InfoResponse (line 454) | type InfoResponse struct
type RegisterDatabaseRequest (line 463) | type RegisterDatabaseRequest struct
type RegisterDatabaseResponse (line 469) | type RegisterDatabaseResponse struct
type UnregisterDatabaseRequest (line 475) | type UnregisterDatabaseRequest struct
type UnregisterDatabaseResponse (line 481) | type UnregisterDatabaseResponse struct
FILE: server_test.go
function testSocketPath (line 23) | func testSocketPath(t *testing.T) string {
function TestServer_HandleInfo (line 31) | func TestServer_HandleInfo(t *testing.T) {
function TestServer_HandleList (line 63) | func TestServer_HandleList(t *testing.T) {
function TestServer_HandleStart (line 220) | func TestServer_HandleStart(t *testing.T) {
function TestServer_HandleStop (line 254) | func TestServer_HandleStop(t *testing.T) {
function TestServer_HandleRegister (line 278) | func TestServer_HandleRegister(t *testing.T) {
function TestServer_HandleUnregister (line 418) | func TestServer_HandleUnregister(t *testing.T) {
function TestServer_HandleSync (line 503) | func TestServer_HandleSync(t *testing.T) {
function newSocketClient (line 659) | func newSocketClient(t *testing.T, socketPath string) *http.Client {
type stringReaderType (line 671) | type stringReaderType struct
method Read (line 680) | func (r *stringReaderType) Read(p []byte) (n int, err error) {
function stringReader (line 676) | func stringReader(s string) *stringReaderType {
FILE: sftp/replica_client.go
function init (line 25) | func init() {
constant ReplicaClientType (line 30) | ReplicaClientType = "sftp"
constant DefaultDialTimeout (line 34) | DefaultDialTimeout = 30 * time.Second
type ReplicaClient (line 40) | type ReplicaClient struct
method SetLogger (line 69) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 99) | func (c *ReplicaClient) Type() string {
method Init (line 104) | func (c *ReplicaClient) Init(ctx context.Context) error {
method init (line 110) | func (c *ReplicaClient) init(ctx context.Context) (_ *sftp.Client, err...
method DeleteAll (line 184) | func (c *ReplicaClient) DeleteAll(ctx context.Context) (err error) {
method LTXFiles (line 228) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method WriteLTXFile (line 267) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method OpenLTXFile (line 327) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method DeleteLTXFiles (line 356) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
method Cleanup (line 379) | func (c *ReplicaClient) Cleanup(ctx context.Context) (err error) {
method resetOnConnError (line 394) | func (c *ReplicaClient) resetOnConnError(err error) {
function NewReplicaClient (line 61) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 76) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
FILE: src/litestream-vfs.c
type sqlite3_api_routines (line 28) | struct sqlite3_api_routines
function sqlite3_litestreamvfs_init (line 31) | int sqlite3_litestreamvfs_init(sqlite3 *db, char **pzErrMsg, const sqlit...
function litestream_auto_extension (line 50) | static void litestream_auto_extension(sqlite3* db, const char** pzErrMsg...
function litestream_register_connection (line 67) | static int litestream_register_connection(sqlite3* db) {
function litestream_set_time_impl (line 89) | static void litestream_set_time_impl(sqlite3_context* ctx, int argc, sql...
function litestream_time_impl (line 125) | static void litestream_time_impl(sqlite3_context* ctx, int argc, sqlite3...
function litestream_txid_impl (line 142) | static void litestream_txid_impl(sqlite3_context* ctx, int argc, sqlite3...
function litestream_lag_impl (line 159) | static void litestream_lag_impl(sqlite3_context* ctx, int argc, sqlite3_...
function litestream_function_destroy (line 175) | static void litestream_function_destroy(void* db) {
FILE: src/sqlite3.h
type sqlite3 (line 272) | typedef struct sqlite3 sqlite3;
type SQLITE_INT64_TYPE (line 291) | typedef SQLITE_INT64_TYPE sqlite_int64;
type SQLITE_UINT64_TYPE (line 293) | typedef SQLITE_UINT64_TYPE sqlite_uint64;
type sqlite_uint64 (line 295) | typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
type __int64 (line 298) | typedef __int64 sqlite_int64;
type sqlite_uint64 (line 299) | typedef unsigned __int64 sqlite_uint64;
type sqlite_int64 (line 301) | typedef long long int sqlite_int64;
type sqlite_uint64 (line 302) | typedef unsigned long long int sqlite_uint64;
type sqlite_int64 (line 304) | typedef sqlite_int64 sqlite3_int64;
type sqlite_uint64 (line 305) | typedef sqlite_uint64 sqlite3_uint64;
type sqlite3_file (line 738) | typedef struct sqlite3_file sqlite3_file;
type sqlite3_file (line 739) | struct sqlite3_file {
type sqlite3_io_methods (line 845) | typedef struct sqlite3_io_methods sqlite3_io_methods;
type sqlite3_io_methods (line 846) | struct sqlite3_io_methods {
type sqlite3_mutex (line 1286) | typedef struct sqlite3_mutex sqlite3_mutex;
type sqlite3_api_routines (line 1296) | typedef struct sqlite3_api_routines sqlite3_api_routines;
type sqlite3_vfs (line 1487) | typedef struct sqlite3_vfs sqlite3_vfs;
type sqlite3_vfs (line 1489) | struct sqlite3_vfs {
type sqlite3_mem_methods (line 1788) | typedef struct sqlite3_mem_methods sqlite3_mem_methods;
type sqlite3_mem_methods (line 1789) | struct sqlite3_mem_methods {
type sqlite3_stmt (line 4222) | typedef struct sqlite3_stmt sqlite3_stmt;
type sqlite3_value (line 4740) | typedef struct sqlite3_value sqlite3_value;
type sqlite3_context (line 4754) | typedef struct sqlite3_context sqlite3_context;
type sqlite3_vtab (line 7499) | typedef struct sqlite3_vtab sqlite3_vtab;
type sqlite3_index_info (line 7500) | typedef struct sqlite3_index_info sqlite3_index_info;
type sqlite3_vtab_cursor (line 7501) | typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
type sqlite3_module (line 7502) | typedef struct sqlite3_module sqlite3_module;
type sqlite3_module (line 7520) | struct sqlite3_module {
type sqlite3_index_info (line 7666) | struct sqlite3_index_info {
type sqlite3_vtab (line 7846) | struct sqlite3_vtab {
type sqlite3_vtab_cursor (line 7870) | struct sqlite3_vtab_cursor {
type sqlite3_blob (line 7916) | typedef struct sqlite3_blob sqlite3_blob;
type sqlite3_mutex_methods (line 8366) | typedef struct sqlite3_mutex_methods sqlite3_mutex_methods;
type sqlite3_mutex_methods (line 8367) | struct sqlite3_mutex_methods {
type sqlite3_str (line 8634) | typedef struct sqlite3_str sqlite3_str;
type sqlite3_pcache (line 9129) | typedef struct sqlite3_pcache sqlite3_pcache;
type sqlite3_pcache_page (line 9141) | typedef struct sqlite3_pcache_page sqlite3_pcache_page;
type sqlite3_pcache_page (line 9142) | struct sqlite3_pcache_page {
type sqlite3_pcache_methods2 (line 9306) | typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
type sqlite3_pcache_methods2 (line 9307) | struct sqlite3_pcache_methods2 {
type sqlite3_pcache_methods (line 9329) | typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
type sqlite3_pcache_methods (line 9330) | struct sqlite3_pcache_methods {
type sqlite3_backup (line 9355) | typedef struct sqlite3_backup sqlite3_backup;
type sqlite3_snapshot (line 10735) | typedef struct sqlite3_snapshot {
type sqlite3_rtree_geometry (line 11103) | typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
type sqlite3_rtree_query_info (line 11104) | typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
type sqlite3_int64 (line 11110) | typedef sqlite3_int64 sqlite3_rtree_dbl;
type sqlite3_rtree_dbl (line 11112) | typedef double sqlite3_rtree_dbl;
type sqlite3_rtree_geometry (line 11133) | struct sqlite3_rtree_geometry {
type sqlite3_rtree_query_info (line 11165) | struct sqlite3_rtree_query_info {
type sqlite3_session (line 11219) | typedef struct sqlite3_session sqlite3_session;
type sqlite3_changeset_iter (line 11227) | typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
type sqlite3_changegroup (line 12093) | typedef struct sqlite3_changegroup sqlite3_changegroup;
type sqlite3_rebaser (line 12739) | typedef struct sqlite3_rebaser sqlite3_rebaser;
type Fts5ExtensionApi (line 13055) | typedef struct Fts5ExtensionApi Fts5ExtensionApi;
type Fts5Context (line 13056) | typedef struct Fts5Context Fts5Context;
type Fts5PhraseIter (line 13057) | typedef struct Fts5PhraseIter Fts5PhraseIter;
type Fts5PhraseIter (line 13067) | struct Fts5PhraseIter {
type Fts5ExtensionApi (line 13368) | struct Fts5ExtensionApi {
type Fts5Tokenizer (line 13649) | typedef struct Fts5Tokenizer Fts5Tokenizer;
type fts5_tokenizer_v2 (line 13650) | typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2;
type fts5_tokenizer_v2 (line 13651) | struct fts5_tokenizer_v2 {
type fts5_tokenizer (line 13677) | typedef struct fts5_tokenizer fts5_tokenizer;
type fts5_tokenizer (line 13678) | struct fts5_tokenizer {
type fts5_api (line 13714) | typedef struct fts5_api fts5_api;
type fts5_api (line 13715) | struct fts5_api {
FILE: src/sqlite3ext.h
type sqlite3_api_routines (line 32) | struct sqlite3_api_routines {
FILE: src/sqlite3vfs.h
type s3vfsFile (line 10) | typedef struct s3vfsFile {
FILE: store.go
type DBNotReadyError (line 42) | type DBNotReadyError struct
method Error (line 46) | func (e *DBNotReadyError) Error() string {
method Is (line 53) | func (e *DBNotReadyError) Is(target error) bool {
constant DefaultSnapshotInterval (line 60) | DefaultSnapshotInterval = 24 * time.Hour
constant DefaultSnapshotRetention (line 61) | DefaultSnapshotRetention = 24 * time.Hour
constant DefaultRetention (line 63) | DefaultRetention = 24 * time.Hour
constant DefaultRetentionCheckInterval (line 64) | DefaultRetentionCheckInterval = 1 * time.Hour
constant DefaultL0Retention (line 68) | DefaultL0Retention = 5 * time.Minute
constant DefaultL0RetentionCheckInterval (line 72) | DefaultL0RetentionCheckInterval = 15 * time.Second
constant DefaultHeartbeatCheckInterval (line 76) | DefaultHeartbeatCheckInterval = 15 * time.Second
constant DefaultDBInitTimeout (line 80) | DefaultDBInitTimeout = 30 * time.Second
type Store (line 87) | type Store struct
method Open (line 167) | func (s *Store) Open(ctx context.Context) error {
method Close (line 236) | func (s *Store) Close(ctx context.Context) (err error) {
method DBs (line 260) | func (s *Store) DBs() []*DB {
method RegisterDB (line 267) | func (s *Store) RegisterDB(db *DB) error {
method UnregisterDB (line 323) | func (s *Store) UnregisterDB(ctx context.Context, path string) error {
method EnableDB (line 358) | func (s *Store) EnableDB(ctx context.Context, path string) error {
method DisableDB (line 381) | func (s *Store) DisableDB(ctx context.Context, path string) error {
method SyncDB (line 409) | func (s *Store) SyncDB(ctx context.Context, path string, wait bool) (S...
method FindDB (line 452) | func (s *Store) FindDB(path string) *DB {
method SetL0Retention (line 466) | func (s *Store) SetL0Retention(d time.Duration) {
method SetDone (line 477) | func (s *Store) SetDone(done <-chan struct{}) {
method SetShutdownSyncTimeout (line 488) | func (s *Store) SetShutdownSyncTimeout(d time.Duration) {
method SetShutdownSyncInterval (line 499) | func (s *Store) SetShutdownSyncInterval(d time.Duration) {
method SetVerifyCompaction (line 510) | func (s *Store) SetVerifyCompaction(v bool) {
method SetRetentionEnabled (line 520) | func (s *Store) SetRetentionEnabled(v bool) {
method SnapshotLevel (line 531) | func (s *Store) SnapshotLevel() *CompactionLevel {
method monitorCompactionLevel (line 538) | func (s *Store) monitorCompactionLevel(ctx context.Context, lvl *Compa...
method monitorL0Retention (line 606) | func (s *Store) monitorL0Retention(ctx context.Context) {
method startHeartbeatMonitorIfNeeded (line 638) | func (s *Store) startHeartbeatMonitorIfNeeded() {
method hasHeartbeatConfigLocked (line 662) | func (s *Store) hasHeartbeatConfigLocked() bool {
method monitorHeartbeats (line 669) | func (s *Store) monitorHeartbeats(ctx context.Context) {
method sendHeartbeatIfNeeded (line 691) | func (s *Store) sendHeartbeatIfNeeded(ctx context.Context) {
method allDatabasesHealthy (line 723) | func (s *Store) allDatabasesHealthy(since time.Time) bool {
method CompactDB (line 745) | func (s *Store) CompactDB(ctx context.Context, db *DB, lvl *Compaction...
method EnforceSnapshotRetention (line 803) | func (s *Store) EnforceSnapshotRetention(ctx context.Context, db *DB) ...
method Validate (line 833) | func (s *Store) Validate(ctx context.Context) (*ValidationResult, erro...
method monitorValidation (line 862) | func (s *Store) monitorValidation(ctx context.Context) {
function NewStore (line 138) | func NewStore(dbs []*DB, levels CompactionLevels) *Store {
type SyncDBResult (line 399) | type SyncDBResult struct
type ValidationResult (line 826) | type ValidationResult struct
FILE: store_compaction_remote_test.go
function TestStore_CompactDB_RemotePartialRead (line 26) | func TestStore_CompactDB_RemotePartialRead(t *testing.T) {
type delayedReplicaClient (line 105) | type delayedReplicaClient struct
method Type (line 128) | func (c *delayedReplicaClient) Type() string { return "delayed" }
method Init (line 130) | func (c *delayedReplicaClient) Init(context.Context) error { return nil }
method SetLogger (line 132) | func (c *delayedReplicaClient) SetLogger(*slog.Logger) {}
method key (line 134) | func (c *delayedReplicaClient) key(level int, min, max ltx.TXID) string {
method LTXFiles (line 138) | func (c *delayedReplicaClient) LTXFiles(_ context.Context, level int, ...
method OpenLTXFile (line 162) | func (c *delayedReplicaClient) OpenLTXFile(_ context.Context, level in...
method WriteLTXFile (line 186) | func (c *delayedReplicaClient) WriteLTXFile(_ context.Context, level i...
method DeleteLTXFiles (line 219) | func (c *delayedReplicaClient) DeleteLTXFiles(_ context.Context, a []*...
method DeleteAll (line 228) | func (c *delayedReplicaClient) DeleteAll(context.Context) error {
method waitForAvailability (line 235) | func (c *delayedReplicaClient) waitForAvailability() {
type delayedFile (line 111) | type delayedFile struct
function newDelayedReplicaClient (line 121) | func newDelayedReplicaClient(delay time.Duration) *delayedReplicaClient {
function buildPartialSnapshot (line 241) | func buildPartialSnapshot(data []byte) ([]byte, error) {
FILE: store_test.go
function TestStore_CompactDB (line 18) | func TestStore_CompactDB(t *testing.T) {
function TestStore_Integration (line 131) | func TestStore_Integration(t *testing.T) {
function TestStore_SnapshotInterval_Default (line 263) | func TestStore_SnapshotInterval_Default(t *testing.T) {
function TestStore_Validate (line 280) | func TestStore_Validate(t *testing.T) {
function TestStore_ValidationMonitor (line 395) | func TestStore_ValidationMonitor(t *testing.T) {
function TestStore_SetRetentionEnabled (line 442) | func TestStore_SetRetentionEnabled(t *testing.T) {
FILE: tests/integration/boundary_test.go
function Test1GBBoundary (line 14) | func Test1GBBoundary(t *testing.T) {
function TestLockPageWithDifferentPageSizes (line 119) | func TestLockPageWithDifferentPageSizes(t *testing.T) {
function containsAny (line 182) | func containsAny(s string, substrs []string) bool {
function contains (line 191) | func contains(s, substr string) bool {
function anySubstring (line 195) | func anySubstring(s, substr string) bool {
FILE: tests/integration/compatibility_test.go
function TestRestore_FormatConsistency (line 25) | func TestRestore_FormatConsistency(t *testing.T) {
function TestRestore_MultipleSyncs (line 121) | func TestRestore_MultipleSyncs(t *testing.T) {
function TestRestore_LTXFileValidation (line 190) | func TestRestore_LTXFileValidation(t *testing.T) {
function TestRestore_CrossPlatformPaths (line 245) | func TestRestore_CrossPlatformPaths(t *testing.T) {
function TestRestore_PointInTimeAccuracy (line 300) | func TestRestore_PointInTimeAccuracy(t *testing.T) {
function createValidLTXData (line 358) | func createValidLTXData(t *testing.T, minTXID, maxTXID ltx.TXID, ts time...
function TestBinaryCompatibility_CLIRestore (line 385) | func TestBinaryCompatibility_CLIRestore(t *testing.T) {
function TestVersionMigration_DirectoryLayout (line 455) | func TestVersionMigration_DirectoryLayout(t *testing.T) {
function TestCompaction_Compatibility (line 554) | func TestCompaction_Compatibility(t *testing.T) {
FILE: tests/integration/comprehensive_soak_test.go
function TestComprehensiveSoak (line 25) | func TestComprehensiveSoak(t *testing.T) {
FILE: tests/integration/concurrent_test.go
function TestRapidCheckpoints (line 17) | func TestRapidCheckpoints(t *testing.T) {
function TestWALGrowth (line 137) | func TestWALGrowth(t *testing.T) {
function TestConcurrentOperations (line 256) | func TestConcurrentOperations(t *testing.T) {
function TestBusyTimeout (line 368) | func TestBusyTimeout(t *testing.T) {
function getFileSize (line 476) | func getFileSize(path string) (int64, error) {
FILE: tests/integration/directory_watcher_helpers.go
type DirWatchTestDB (line 20) | type DirWatchTestDB struct
method CreateDirectoryWatchConfig (line 52) | func (db *DirWatchTestDB) CreateDirectoryWatchConfig() (string, error) {
function SetupDirectoryWatchTest (line 30) | func SetupDirectoryWatchTest(t *testing.T, name string, pattern string, ...
function CreateDatabaseInDir (line 79) | func CreateDatabaseInDir(t *testing.T, dirPath, subDir, name string) str...
function CreateDatabaseWithData (line 113) | func CreateDatabaseWithData(t *testing.T, dbPath string, rowCount int) e...
function CreateFakeDatabase (line 157) | func CreateFakeDatabase(t *testing.T, dirPath, name string, content []by...
function WaitForDatabaseInReplica (line 170) | func WaitForDatabaseInReplica(t *testing.T, replicaPath, dbPath string, ...
function VerifyDatabaseRemoved (line 218) | func VerifyDatabaseRemoved(t *testing.T, replicaPath, dbPath string, tim...
function CountDatabasesInReplica (line 244) | func CountDatabasesInReplica(replicaPath string) (int, error) {
function StartContinuousWrites (line 270) | func StartContinuousWrites(ctx context.Context, t *testing.T, dbPath str...
function CreateMultipleDatabasesConcurrently (line 314) | func CreateMultipleDatabasesConcurrently(t *testing.T, dirPath string, c...
function GetRowCount (line 340) | func GetRowCount(dbPath, tableName string) (int, error) {
function getRelativeDBPath (line 358) | func getRelativeDBPath(dbPath, replicaBase string) (string, error) {
function hasLTXFiles (line 364) | func hasLTXFiles(dir string) (bool, error) {
function CheckForCriticalErrors (line 379) | func CheckForCriticalErrors(t *testing.T, db *TestDB) ([]string, error) {
FILE: tests/integration/directory_watcher_test.go
function TestDirectoryWatcherBasicLifecycle (line 18) | func TestDirectoryWatcherBasicLifecycle(t *testing.T) {
function TestDirectoryWatcherRapidConcurrentCreation (line 112) | func TestDirectoryWatcherRapidConcurrentCreation(t *testing.T) {
function TestDirectoryWatcherRecursiveMode (line 165) | func TestDirectoryWatcherRecursiveMode(t *testing.T) {
function TestDirectoryWatcherPatternMatching (line 233) | func TestDirectoryWatcherPatternMatching(t *testing.T) {
function TestDirectoryWatcherNonSQLiteRejection (line 293) | func TestDirectoryWatcherNonSQLiteRejection(t *testing.T) {
function TestDirectoryWatcherActiveConnections (line 350) | func TestDirectoryWatcherActiveConnections(t *testing.T) {
function TestDirectoryWatcherRestartBehavior (line 433) | func TestDirectoryWatcherRestartBehavior(t *testing.T) {
function TestDirectoryWatcherRenameOperations (line 529) | func TestDirectoryWatcherRenameOperations(t *testing.T) {
function TestDirectoryWatcherLoadWithWrites (line 581) | func TestDirectoryWatcherLoadWithWrites(t *testing.T) {
FILE: tests/integration/docker_helpers.go
function RequireDocker (line 14) | func RequireDocker(t *testing.T) {
function StartMinioTestContainer (line 21) | func StartMinioTestContainer(t *testing.T) (string, string) {
function StopMinioTestContainer (line 46) | func StopMinioTestContainer(t *testing.T, name string) {
function runDockerCommand (line 58) | func runDockerCommand(t *testing.T, args ...string) string {
function parseDockerPort (line 68) | func parseDockerPort(t *testing.T, portInfo string) string {
FILE: tests/integration/fixtures.go
type LoadPattern (line 14) | type LoadPattern
constant LoadPatternConstant (line 17) | LoadPatternConstant LoadPattern = "constant"
constant LoadPatternBurst (line 18) | LoadPatternBurst LoadPattern = "burst"
constant LoadPatternRandom (line 19) | LoadPatternRandom LoadPattern = "random"
constant LoadPatternWave (line 20) | LoadPatternWave LoadPattern = "wave"
type LoadConfig (line 23) | type LoadConfig struct
function DefaultLoadConfig (line 32) | func DefaultLoadConfig() *LoadConfig {
type PopulateConfig (line 43) | type PopulateConfig struct
function DefaultPopulateConfig (line 52) | func DefaultPopulateConfig() *PopulateConfig {
function CreateComplexTestSchema (line 63) | func CreateComplexTestSchema(db *sql.DB) error {
function PopulateComplexTestData (line 103) | func PopulateComplexTestData(db *sql.DB, userCount, postsPerUser, commen...
function generateRandomContent (line 183) | func generateRandomContent(length int) string {
type TestScenario (line 195) | type TestScenario struct
function LargeWALScenario (line 202) | func LargeWALScenario() *TestScenario {
function RapidCheckpointsScenario (line 247) | func RapidCheckpointsScenario() *TestScenario {
FILE: tests/integration/helpers.go
type TestDB (line 24) | type TestDB struct
method Create (line 109) | func (db *TestDB) Create() error {
method CreateWithPageSize (line 123) | func (db *TestDB) CreateWithPageSize(pageSize int) error {
method Populate (line 141) | func (db *TestDB) Populate(targetSize string) error {
method PopulateWithOptions (line 160) | func (db *TestDB) PopulateWithOptions(targetSize string, pageSize int,...
method GenerateLoad (line 181) | func (db *TestDB) GenerateLoad(ctx context.Context, writeRate int, dur...
method StartLitestream (line 202) | func (db *TestDB) StartLitestream() error {
method StartLitestreamWithConfig (line 235) | func (db *TestDB) StartLitestreamWithConfig(configPath string) error {
method StopLitestream (line 262) | func (db *TestDB) StopLitestream() error {
method Restore (line 277) | func (db *TestDB) Restore(outputPath string) error {
method Validate (line 303) | func (db *TestDB) Validate(restoredPath string) error {
method QuickValidate (line 322) | func (db *TestDB) QuickValidate(restoredPath string) error {
method GetRowCount (line 341) | func (db *TestDB) GetRowCount(table string) (int, error) {
method GetDatabaseSize (line 357) | func (db *TestDB) GetDatabaseSize() (int64, error) {
method GetReplicaFileCount (line 373) | func (db *TestDB) GetReplicaFileCount() (int, error) {
method WaitForReplicaFiles (line 382) | func (db *TestDB) WaitForReplicaFiles(minFiles int, timeout time.Durat...
method GetLitestreamLog (line 398) | func (db *TestDB) GetLitestreamLog() (string, error) {
method CheckForErrors (line 407) | func (db *TestDB) CheckForErrors() ([]string, error) {
method Cleanup (line 424) | func (db *TestDB) Cleanup() {
method WaitForSnapshots (line 429) | func (db *TestDB) WaitForSnapshots(timeout time.Duration) error {
method IntegrityCheck (line 564) | func (db *TestDB) IntegrityCheck() error {
method PrintTestSummary (line 582) | func (db *TestDB) PrintTestSummary(t *testing.T, testName string, star...
function getBinaryPath (line 38) | func getBinaryPath(name string) string {
function streamCommandOutput (line 46) | func streamCommandOutput() bool {
function configureCmdIO (line 56) | func configureCmdIO(cmd *exec.Cmd) (bool, *bytes.Buffer, *bytes.Buffer) {
function combinedOutput (line 70) | func combinedOutput(stdoutBuf, stderrBuf *bytes.Buffer) string {
function SetupTestDB (line 81) | func SetupTestDB(t *testing.T, name string) *TestDB {
function countLTXFiles (line 454) | func countLTXFiles(dir string) int {
function GetTestDuration (line 462) | func GetTestDuration(t *testing.T, defaultDuration time.Duration) time.D...
function RequireBinaries (line 472) | func RequireBinaries(t *testing.T) {
function WriteS3AccessPointConfig (line 487) | func WriteS3AccessPointConfig(t *testing.T, dbPath, replicaURL, endpoint...
function CreateTestTable (line 514) | func CreateTestTable(t *testing.T, dbPath string) error {
function InsertTestData (line 533) | func InsertTestData(t *testing.T, dbPath string, count int) error {
FILE: tests/integration/minio_soak_test.go
function TestMinIOSoak (line 32) | func TestMinIOSoak(t *testing.T) {
function logMinIOMetrics (line 304) | func logMinIOMetrics(t *testing.T, db *TestDB, containerID, bucket strin...
function countMinIOLTXFiles (line 323) | func countMinIOLTXFiles(t *testing.T, containerID, bucket string) int {
function getRowCountFromPath (line 348) | func getRowCountFromPath(dbPath, table string) (int, error) {
FILE: tests/integration/overnight_s3_soak_test.go
function TestOvernightS3Soak (line 33) | func TestOvernightS3Soak(t *testing.T) {
function logS3Metrics (line 314) | func logS3Metrics(t *testing.T, db *TestDB, s3URL string) {
function getRowCountFromPath (line 333) | func getRowCountFromPath(dbPath, table string) (int, error) {
FILE: tests/integration/overnight_test.go
function TestOvernightFile (line 14) | func TestOvernightFile(t *testing.T) {
function TestOvernightComprehensive (line 132) | func TestOvernightComprehensive(t *testing.T) {
FILE: tests/integration/profile_test.go
function TestIdleCPUProfile (line 42) | func TestIdleCPUProfile(t *testing.T) {
FILE: tests/integration/quick_test.go
function TestQuickValidation (line 14) | func TestQuickValidation(t *testing.T) {
FILE: tests/integration/s3_access_point_test.go
function TestS3AccessPointLocalStack (line 23) | func TestS3AccessPointLocalStack(t *testing.T) {
function newMinioS3Client (line 99) | func newMinioS3Client(t *testing.T, endpoint string, forcePathStyle bool...
function createBucket (line 125) | func createBucket(t *testing.T, ctx context.Context, client *awss3.Clien...
function clearBucket (line 133) | func clearBucket(ctx context.Context, client *awss3.Client, bucket strin...
function waitForObjects (line 157) | func waitForObjects(t *testing.T, client *awss3.Client, bucket, prefix s...
function compareRowCounts (line 187) | func compareRowCounts(srcPath, restoredPath string) error {
function findUserTable (line 218) | func findUserTable(db *sql.DB) (string, error) {
FILE: tests/integration/s3_restore_connection_drop_test.go
function TestRestore_S3ConnectionDrop (line 23) | func TestRestore_S3ConnectionDrop(t *testing.T) {
function startDockerNetwork (line 99) | func startDockerNetwork(t *testing.T) string {
function removeDockerNetwork (line 106) | func removeDockerNetwork(name string) {
function startMinioContainerForProxy (line 113) | func startMinioContainerForProxy(t *testing.T, networkName string) string {
function startToxiproxyContainer (line 130) | func startToxiproxyContainer(t *testing.T, networkName string) (string, ...
function stopDockerContainer (line 156) | func stopDockerContainer(name string) {
function createMinioBucket (line 163) | func createMinioBucket(t *testing.T, networkName, minioName, bucket stri...
function writeS3Config (line 176) | func writeS3Config(t *testing.T, dbPath, replicaURL, endpoint string) st...
function insertLargeRows (line 203) | func insertLargeRows(dbPath string, rows int, blobSize int) error {
function verifyRestoredRowCount (line 223) | func verifyRestoredRowCount(dbPath string, expected int) error {
type toxiproxyClient (line 240) | type toxiproxyClient struct
method createProxy (line 255) | func (c *toxiproxyClient) createProxy(t *testing.T, name, listen, upst...
method addResetPeerToxic (line 265) | func (c *toxiproxyClient) addResetPeerToxic(t *testing.T, proxy, name ...
method removeToxic (line 279) | func (c *toxiproxyClient) removeToxic(t *testing.T, proxy, name string) {
method postJSON (line 296) | func (c *toxiproxyClient) postJSON(t *testing.T, path string, payload ...
function newToxiproxyClient (line 245) | func newToxiproxyClient(t *testing.T, baseURL string) *toxiproxyClient {
FILE: tests/integration/scenario_test.go
function TestFreshStart (line 16) | func TestFreshStart(t *testing.T) {
function TestDatabaseIntegrity (line 137) | func TestDatabaseIntegrity(t *testing.T) {
function TestDatabaseDeletion (line 243) | func TestDatabaseDeletion(t *testing.T) {
function min (line 337) | func min(a, b int) int {
FILE: tests/integration/shutdown_retry_test.go
function TestShutdownSyncRetry_429Errors (line 42) | func TestShutdownSyncRetry_429Errors(t *testing.T) {
type rateLimitingProxy (line 258) | type rateLimitingProxy struct
method ServeHTTP (line 299) | func (p *rateLimitingProxy) ServeHTTP(w http.ResponseWriter, r *http.R...
method Reset (line 319) | func (p *rateLimitingProxy) Reset() {
method Stats (line 323) | func (p *rateLimitingProxy) Stats() proxyStats {
type proxyStats (line 270) | type proxyStats struct
function newRateLimitingProxy (line 276) | func newRateLimitingProxy(t *testing.T, targetURL string, limit int) *ra...
FILE: tests/integration/soak_helpers.go
type S3Config (line 22) | type S3Config struct
type TestInfo (line 34) | type TestInfo struct
type ErrorStats (line 44) | type ErrorStats struct
function isInteractive (line 52) | func isInteractive() bool {
function promptYesNo (line 59) | func promptYesNo(t *testing.T, prompt string, defaultYes bool) bool {
function promptYesNoDefaultNo (line 106) | func promptYesNoDefaultNo(t *testing.T, prompt string) bool {
function promptYesNoDefaultYes (line 110) | func promptYesNoDefaultYes(t *testing.T, prompt string) bool {
function StartMinIOContainer (line 115) | func StartMinIOContainer(t *testing.T) (containerID string, endpoint str...
function StopMinIOContainer (line 167) | func StopMinIOContainer(t *testing.T, containerID string, volumeName str...
function CreateMinIOBucket (line 185) | func CreateMinIOBucket(t *testing.T, containerID, bucket string) {
function minioBucketExists (line 224) | func minioBucketExists(containerID, bucket string) bool {
function clearMinIOBucket (line 236) | func clearMinIOBucket(containerID, bucket string) error {
function waitForMinIOBucket (line 252) | func waitForMinIOBucket(containerID, bucket string, timeout time.Duratio...
function CountMinIOObjects (line 267) | func CountMinIOObjects(t *testing.T, containerID, bucket string) int {
function CheckAWSCredentials (line 290) | func CheckAWSCredentials(t *testing.T) (bucket, region string) {
function TestS3Connectivity (line 312) | func TestS3Connectivity(t *testing.T, bucket string) {
function CountS3Objects (line 324) | func CountS3Objects(t *testing.T, s3URL string) int {
function GetS3StorageSize (line 343) | func GetS3StorageSize(t *testing.T, s3URL string) int64 {
function CreateSoakConfig (line 366) | func CreateSoakConfig(dbPath, replicaURL string, s3Config *S3Config, sho...
function setupSignalHandler (line 445) | func setupSignalHandler(t *testing.T, cancel context.CancelFunc, testInf...
function performGracefulShutdown (line 512) | func performGracefulShutdown(t *testing.T, testInfo *TestInfo) {
function getErrorStats (line 602) | func getErrorStats(db *TestDB) ErrorStats {
function printProgress (line 638) | func printProgress(t *testing.T, elapsed, total time.Duration, errorStat...
function printErrorDetails (line 694) | func printErrorDetails(t *testing.T, errorStats ErrorStats) {
function shouldAbortTest (line 735) | func shouldAbortTest(errorStats ErrorStats, fileCount int, elapsed time....
function MonitorSoakTest (line 761) | func MonitorSoakTest(t *testing.T, db *TestDB, ctx context.Context, info...
function LogSoakMetrics (line 872) | func LogSoakMetrics(t *testing.T, db *TestDB, testName string) {
type SoakTestAnalysis (line 910) | type SoakTestAnalysis struct
function AnalyzeSoakTest (line 927) | func AnalyzeSoakTest(t *testing.T, db *TestDB, duration time.Duration) *...
function parseLog (line 965) | func parseLog(logPath string, analysis *SoakTestAnalysis) {
function PrintSoakTestAnalysis (line 1026) | func PrintSoakTestAnalysis(t *testing.T, analysis *SoakTestAnalysis) {
FILE: tests/integration/upgrade_test.go
function TestUpgrade_V3ToV5 (line 20) | func TestUpgrade_V3ToV5(t *testing.T) {
FILE: v3.go
type PosV3 (line 14) | type PosV3 struct
method IsZero (line 21) | func (p PosV3) IsZero() bool {
method String (line 26) | func (p PosV3) String() string {
type SnapshotInfoV3 (line 34) | type SnapshotInfoV3 struct
method Pos (line 42) | func (info SnapshotInfoV3) Pos() PosV3 {
type WALSegmentInfoV3 (line 47) | type WALSegmentInfoV3 struct
method Pos (line 56) | func (info WALSegmentInfoV3) Pos() PosV3 {
constant GenerationsDirV3 (line 62) | GenerationsDirV3 = "generations"
constant SnapshotsDirV3 (line 63) | SnapshotsDirV3 = "snapshots"
constant WALDirV3 (line 64) | WALDirV3 = "wal"
function GenerationsPathV3 (line 68) | func GenerationsPathV3(root string) string {
function GenerationPathV3 (line 73) | func GenerationPathV3(root, generation string) string {
function SnapshotsPathV3 (line 78) | func SnapshotsPathV3(root, generation string) string {
function WALPathV3 (line 83) | func WALPathV3(root, generation string) string {
function SnapshotPathV3 (line 88) | func SnapshotPathV3(root, generation string, index int) string {
function WALSegmentPathV3 (line 93) | func WALSegmentPathV3(root, generation string, index int, offset int64) ...
function FormatSnapshotFilenameV3 (line 99) | func FormatSnapshotFilenameV3(index int) string {
function FormatWALSegmentFilenameV3 (line 105) | func FormatWALSegmentFilenameV3(index int, offset int64) string {
function ParseSnapshotFilenameV3 (line 117) | func ParseSnapshotFilenameV3(filename string) (index int, err error) {
function ParseWALSegmentFilenameV3 (line 128) | func ParseWALSegmentFilenameV3(filename string) (index int, offset int64...
function IsGenerationIDV3 (line 139) | func IsGenerationIDV3(s string) bool {
type ReplicaClientV3 (line 145) | type ReplicaClientV3 interface
FILE: v3_test.go
function TestPosV3_IsZero (line 9) | func TestPosV3_IsZero(t *testing.T) {
function TestPosV3_String (line 18) | func TestPosV3_String(t *testing.T) {
function TestSnapshotInfoV3_Pos (line 33) | func TestSnapshotInfoV3_Pos(t *testing.T) {
function TestWALSegmentInfoV3_Pos (line 41) | func TestWALSegmentInfoV3_Pos(t *testing.T) {
function TestFormatSnapshotFilenameV3 (line 49) | func TestFormatSnapshotFilenameV3(t *testing.T) {
function TestParseSnapshotFilenameV3 (line 66) | func TestParseSnapshotFilenameV3(t *testing.T) {
function TestFormatWALSegmentFilenameV3 (line 109) | func TestFormatWALSegmentFilenameV3(t *testing.T) {
function TestParseWALSegmentFilenameV3 (line 126) | func TestParseWALSegmentFilenameV3(t *testing.T) {
function TestIsGenerationIDV3 (line 168) | func TestIsGenerationIDV3(t *testing.T) {
function TestPathsV3 (line 192) | func TestPathsV3(t *testing.T) {
function TestFormatParseRoundtrip (line 246) | func TestFormatParseRoundtrip(t *testing.T) {
FILE: vfs.go
constant DefaultPollInterval (line 31) | DefaultPollInterval = 1 * time.Second
constant DefaultCacheSize (line 32) | DefaultCacheSize = 10 * 1024 * 1024
constant DefaultPageSize (line 33) | DefaultPageSize = 4096
constant pageFetchRetryAttempts (line 35) | pageFetchRetryAttempts = 6
constant pageFetchRetryDelay (line 36) | pageFetchRetryDelay = 15 * time.Millisecond
type VFS (line 55) | type VFS struct
method Open (line 127) | func (vfs *VFS) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3v...
method openMainDB (line 140) | func (vfs *VFS) openMainDB(name string, flags sqlite3vfs.OpenFlag) (sq...
method Delete (line 221) | func (vfs *VFS) Delete(name string, dirSync bool) error {
method Access (line 236) | func (vfs *VFS) Access(name string, flag sqlite3vfs.AccessFlag) (bool,...
method accessWAL (line 248) | func (vfs *VFS) accessWAL(name string, flag sqlite3vfs.AccessFlag) (bo...
method FullPathname (line 252) | func (vfs *VFS) FullPathname(name string) string {
method requiresTempFile (line 257) | func (vfs *VFS) requiresTempFile(flags sqlite3vfs.OpenFlag) bool {
method ensureTempDir (line 270) | func (vfs *VFS) ensureTempDir() (string, error) {
method canonicalTempName (line 282) | func (vfs *VFS) canonicalTempName(name string) string {
method openTempFile (line 306) | func (vfs *VFS) openTempFile(name string, flags sqlite3vfs.OpenFlag) (...
method deleteTempFile (line 343) | func (vfs *VFS) deleteTempFile(name string) error {
method isTempFileName (line 361) | func (vfs *VFS) isTempFileName(name string) bool {
method wasTempFileName (line 366) | func (vfs *VFS) wasTempFileName(name string) bool {
method unregisterTempFile (line 375) | func (vfs *VFS) unregisterTempFile(name string) {
method accessTempFile (line 383) | func (vfs *VFS) accessTempFile(name string, flag sqlite3vfs.AccessFlag...
method trackTempFile (line 398) | func (vfs *VFS) trackTempFile(canonical, path string) func() {
method loadTempFilePath (line 407) | func (vfs *VFS) loadTempFilePath(name string) (string, bool) {
function NewVFS (line 118) | func NewVFS(client ReplicaClient, logger *slog.Logger) *VFS {
function tempFilenameFromCanonical (line 293) | func tempFilenameFromCanonical(canonical string) (string, error) {
function openFlagToOSFlag (line 418) | func openFlagToOSFlag(flag sqlite3vfs.OpenFlag) int {
type localTempFile (line 439) | type localTempFile struct
method Close (line 450) | func (tf *localTempFile) Close() error {
method ReadAt (line 463) | func (tf *localTempFile) ReadAt(p []byte, off int64) (n int, err error) {
method WriteAt (line 467) | func (tf *localTempFile) WriteAt(b []byte, off int64) (n int, err erro...
method Truncate (line 471) | func (tf *localTempFile) Truncate(size int64) error {
method Sync (line 475) | func (tf *localTempFile) Sync(flag sqlite3vfs.SyncType) error {
method FileSize (line 479) | func (tf *localTempFile) FileSize() (int64, error) {
method Lock (line 487) | func (tf *localTempFile) Lock(elock sqlite3vfs.LockType) error {
method Unlock (line 495) | func (tf *localTempFile) Unlock(elock sqlite3vfs.LockType) error {
method CheckReservedLock (line 500) | func (tf *localTempFile) CheckReservedLock() (bool, error) {
method SectorSize (line 504) | func (tf *localTempFile) SectorSize() int64 {
method DeviceCharacteristics (line 508) | func (tf *localTempFile) DeviceCharacteristics() sqlite3vfs.DeviceChar...
function newLocalTempFile (line 446) | func newLocalTempFile(f *os.File, deleteOnClose bool, onClose func()) *l...
type VFSFile (line 513) | type VFSFile struct
method Pos (line 969) | func (f *VFSFile) Pos() ltx.Pos {
method MaxTXID1 (line 976) | func (f *VFSFile) MaxTXID1() ltx.TXID {
method LockType (line 983) | func (f *VFSFile) LockType() sqlite3vfs.LockType {
method TargetTime (line 990) | func (f *VFSFile) TargetTime() *time.Time {
method LatestLTXTime (line 1001) | func (f *VFSFile) LatestLTXTime() time.Time {
method LastPollSuccess (line 1008) | func (f *VFSFile) LastPollSuccess() time.Time {
method hasTargetTime (line 1014) | func (f *VFSFile) hasTargetTime() bool {
method Open (line 1020) | func (f *VFSFile) Open() error {
method openNewDatabase (line 1110) | func (f *VFSFile) openNewDatabase() error {
method SetTargetTime (line 1166) | func (f *VFSFile) SetTargetTime(ctx context.Context, timestamp time.Ti...
method ResetTime (line 1188) | func (f *VFSFile) ResetTime(ctx context.Context) error {
method rebuildIndex (line 1200) | func (f *VFSFile) rebuildIndex(ctx context.Context, infos []*ltx.FileI...
method buildIndexMap (line 1251) | func (f *VFSFile) buildIndexMap(ctx context.Context, infos []*ltx.File...
method buildIndex (line 1283) | func (f *VFSFile) buildIndex(ctx context.Context, infos []*ltx.FileInf...
method initHydration (line 1288) | func (f *VFSFile) initHydration(infos []*ltx.FileInfo) error {
method runHydration (line 1302) | func (f *VFSFile) runHydration(infos []*ltx.FileInfo) {
method applySyncedPagesToHydratedFile (line 1350) | func (f *VFSFile) applySyncedPagesToHydratedFile() error {
method Close (line 1366) | func (f *VFSFile) Close() error {
method ReadAt (line 1422) | func (f *VFSFile) ReadAt(p []byte, off int64) (n int, err error) {
method WriteAt (line 1542) | func (f *VFSFile) WriteAt(b []byte, off int64) (n int, err error) {
method readPageForWrite (line 1602) | func (f *VFSFile) readPageForWrite(pgno uint32, buf []byte) error {
method Truncate (line 1632) | func (f *VFSFile) Truncate(size int64) error {
method Sync (line 1671) | func (f *VFSFile) Sync(flag sqlite3vfs.SyncType) error {
method SetWriteEnabled (line 1697) | func (f *VFSFile) SetWriteEnabled(enabled bool) error {
method SetWriteEnabledWithTimeout (line 1715) | func (f *VFSFile) SetWriteEnabledWithTimeout(enabled bool, timeout tim...
method syncLoop (line 1871) | func (f *VFSFile) syncLoop(stopCh <-chan struct{}, tickerCh <-chan tim...
method syncToRemote (line 1892) | func (f *VFSFile) syncToRemote() error {
method syncToRemoteWithLock (line 1900) | func (f *VFSFile) syncToRemoteWithLock() error {
method checkForConflict (line 1971) | func (f *VFSFile) checkForConflict(ctx context.Context) error {
method createLTXFromDirty (line 2006) | func (f *VFSFile) createLTXFromDirty() io.Reader {
method initWriteBuffer (line 2088) | func (f *VFSFile) initWriteBuffer() error {
method initWriteBufferWithLock (line 2097) | func (f *VFSFile) initWriteBufferWithLock() error {
method writeToBuffer (line 2118) | func (f *VFSFile) writeToBuffer(pgno uint32, data []byte) error {
method clearWriteBuffer (line 2141) | func (f *VFSFile) clearWriteBuffer() error {
method FileSize (line 2153) | func (f *VFSFile) FileSize() (size int64, err error) {
method Lock (line 2182) | func (f *VFSFile) Lock(elock sqlite3vfs.LockType) error {
method Unlock (line 2231) | func (f *VFSFile) Unlock(elock sqlite3vfs.LockType) error {
method CheckReservedLock (line 2279) | func (f *VFSFile) CheckReservedLock() (bool, error) {
method SectorSize (line 2295) | func (f *VFSFile) SectorSize() int64 {
method DeviceCharacteristics (line 2300) | func (f *VFSFile) DeviceCharacteristics() sqlite3vfs.DeviceCharacteris...
method FileControl (line 2332) | func (f *VFSFile) FileControl(op int, pragmaName string, pragmaValue *...
method currentTimeString (line 2441) | func (f *VFSFile) currentTimeString() string {
method monitorReplicaClient (line 2471) | func (f *VFSFile) monitorReplicaClient(ctx context.Context) {
method pollReplicaClient (line 2500) | func (f *VFSFile) pollReplicaClient(ctx context.Context) error {
method pollLevel (line 2626) | func (f *VFSFile) pollLevel(ctx context.Context, level int, prevMaxTXI...
method pageSizeBytes (line 2681) | func (f *VFSFile) pageSizeBytes() (uint32, error) {
method waitForRestorePlan (line 2733) | func (f *VFSFile) waitForRestorePlan() ([]*ltx.FileInfo, error) {
method startCompactionMonitors (line 2861) | func (f *VFSFile) startCompactionMonitors() {
method Compact (line 2908) | func (f *VFSFile) Compact(ctx context.Context, level int) (*ltx.FileIn...
method Snapshot (line 2917) | func (f *VFSFile) Snapshot(ctx context.Context) (*ltx.FileInfo, error) {
method monitorCompaction (line 2990) | func (f *VFSFile) monitorCompaction(ctx context.Context, lvl *Compacti...
method monitorSnapshots (line 3020) | func (f *VFSFile) monitorSnapshots(ctx context.Context) {
method monitorL0Retention (line 3056) | func (f *VFSFile) monitorL0Retention(ctx context.Context) {
type Hydrator (line 568) | type Hydrator struct
method Init (line 594) | func (h *Hydrator) Init() error {
method Complete (line 625) | func (h *Hydrator) Complete() bool {
method SetComplete (line 630) | func (h *Hydrator) SetComplete() {
method Disable (line 635) | func (h *Hydrator) Disable() {
method TXID (line 640) | func (h *Hydrator) TXID() ltx.TXID {
method SetTXID (line 647) | func (h *Hydrator) SetTXID(txid ltx.TXID) {
method Err (line 654) | func (h *Hydrator) Err() error {
method SetErr (line 661) | func (h *Hydrator) SetErr(err error) {
method Status (line 668) | func (h *Hydrator) Status() ltx.CompactorStatus {
method Restore (line 676) | func (h *Hydrator) Restore(ctx context.Context, infos []*ltx.FileInfo)...
method CatchUp (line 726) | func (h *Hydrator) CatchUp(ctx context.Context, fromTXID, toTXID ltx.T...
method ApplyLTX (line 755) | func (h *Hydrator) ApplyLTX(ctx context.Context, info *ltx.FileInfo) e...
method ReadAt (line 793) | func (h *Hydrator) ReadAt(p []byte, off int64) (int, error) {
method ApplyUpdates (line 812) | func (h *Hydrator) ApplyUpdates(ctx context.Context, updates map[uint3...
method WritePage (line 832) | func (h *Hydrator) WritePage(pgno uint32, data []byte) error {
method Truncate (line 844) | func (h *Hydrator) Truncate(size int64) error {
method Close (line 852) | func (h *Hydrator) Close() error {
method metaPath (line 880) | func (h *Hydrator) metaPath() string {
method loadMeta (line 884) | func (h *Hydrator) loadMeta() (ltx.TXID, error) {
method saveMeta (line 896) | func (h *Hydrator) saveMeta() error {
function NewHydrator (line 583) | func NewHydrator(path string, persistent bool, pageSize uint32, client R...
function syncDir (line 944) | func syncDir(path string) error {
function NewVFSFile (line 953) | func NewVFSFile(client ReplicaClient, name string, logger *slog.Logger) ...
function maxLevelTXID (line 1240) | func maxLevelTXID(infos []*ltx.FileInfo, level int) ltx.TXID {
function parseTimeValue (line 2306) | func parseTimeValue(value string) (time.Time, error) {
function isRetryablePageError (line 2451) | func isRetryablePageError(err error) bool {
function detectPageSizeFromInfos (line 2692) | func detectPageSizeFromInfos(ctx context.Context, client ReplicaClient, ...
function readPageSizeFromInfo (line 2711) | func readPageSizeFromInfo(ctx context.Context, client ReplicaClient, inf...
function isSupportedPageSize (line 2724) | func isSupportedPageSize(pageSize uint32) bool {
function RegisterVFSConnection (line 2764) | func RegisterVFSConnection(dbPtr uintptr, fileID uint64) error {
function UnregisterVFSConnection (line 2773) | func UnregisterVFSConnection(dbPtr uintptr) {
function SetVFSConnectionTime (line 2778) | func SetVFSConnectionTime(dbPtr uintptr, timestamp string) error {
function ResetVFSConnectionTime (line 2792) | func ResetVFSConnectionTime(dbPtr uintptr) error {
function GetVFSConnectionTime (line 2801) | func GetVFSConnectionTime(dbPtr uintptr) (string, error) {
function GetVFSConnectionTXID (line 2810) | func GetVFSConnectionTXID(dbPtr uintptr) (string, error) {
function GetVFSConnectionLag (line 2819) | func GetVFSConnectionLag(dbPtr uintptr) (int64, error) {
function vfsFileForConnection (line 2831) | func vfsFileForConnection(dbPtr uintptr) (*VFSFile, error) {
function lookupVFSFile (line 2847) | func lookupVFSFile(fileID uint64) (*VFSFile, bool) {
FILE: vfs_compaction_test.go
function TestVFSFile_Compact (line 16) | func TestVFSFile_Compact(t *testing.T) {
function TestVFSFile_Snapshot (line 53) | func TestVFSFile_Snapshot(t *testing.T) {
function TestDefaultCompactionLevels (line 102) | func TestDefaultCompactionLevels(t *testing.T) {
function TestVFS_CompactionConfig (line 146) | func TestVFS_CompactionConfig(t *testing.T) {
FILE: vfs_test.go
function TestVFSFile_LockStateMachine (line 24) | func TestVFSFile_LockStateMachine(t *testing.T) {
function TestVFSFile_PendingIndexIsolation (line 66) | func TestVFSFile_PendingIndexIsolation(t *testing.T) {
function TestVFSFile_PendingIndexRace (line 116) | func TestVFSFile_PendingIndexRace(t *testing.T) {
function TestVFSFileMonitorStopsOnCancel (line 176) | func TestVFSFileMonitorStopsOnCancel(t *testing.T) {
function TestVFSFile_NonContiguousTXIDError (line 209) | func TestVFSFile_NonContiguousTXIDError(t *testing.T) {
function TestVFSFile_IndexMemoryDoesNotGrowUnbounded (line 227) | func TestVFSFile_IndexMemoryDoesNotGrowUnbounded(t *testing.T) {
function TestVFSFile_AutoVacuumShrinksCommit (line 252) | func TestVFSFile_AutoVacuumShrinksCommit(t *testing.T) {
function TestVFSFile_PendingIndexReplacementRemovesStalePages (line 281) | func TestVFSFile_PendingIndexReplacementRemovesStalePages(t *testing.T) {
function TestVFSFile_CorruptedPageIndexRecovery (line 327) | func TestVFSFile_CorruptedPageIndexRecovery(t *testing.T) {
function TestVFSFile_OpenSeedsLevel1Position (line 337) | func TestVFSFile_OpenSeedsLevel1Position(t *testing.T) {
function TestVFSFile_OpenSeedsLevel1PositionFromPos (line 363) | func TestVFSFile_OpenSeedsLevel1PositionFromPos(t *testing.T) {
function TestVFSFile_HeaderForcesDeleteJournal (line 387) | func TestVFSFile_HeaderForcesDeleteJournal(t *testing.T) {
function TestVFSFile_ReadAtLockPageBoundary (line 406) | func TestVFSFile_ReadAtLockPageBoundary(t *testing.T) {
function TestVFS_TempFileLifecycleStress (line 453) | func TestVFS_TempFileLifecycleStress(t *testing.T) {
function TestVFS_TempFileNameCollision (line 554) | func TestVFS_TempFileNameCollision(t *testing.T) {
function TestVFS_TempFileSameBasenameDifferentDirs (line 596) | func TestVFS_TempFileSameBasenameDifferentDirs(t *testing.T) {
function TestVFS_TempFileDeleteOnClose (line 649) | func TestVFS_TempFileDeleteOnClose(t *testing.T) {
function TestLocalTempFileLocking (line 684) | func TestLocalTempFileLocking(t *testing.T) {
function TestVFS_DeleteIgnoresMissingTempFiles (line 731) | func TestVFS_DeleteIgnoresMissingTempFiles(t *testing.T) {
function TestVFS_TempDirExhaustion (line 780) | func TestVFS_TempDirExhaustion(t *testing.T) {
function TestVFSFile_PollingCancelsBlockedLTXFiles (line 794) | func TestVFSFile_PollingCancelsBlockedLTXFiles(t *testing.T) {
type mockReplicaClient (line 830) | type mockReplicaClient struct
method Type (line 882) | func (c *mockReplicaClient) Type() string { return "mock" }
method Init (line 884) | func (c *mockReplicaClient) Init(context.Context) error { return nil }
method addFixture (line 886) | func (c *mockReplicaClient) addFixture(tb testing.TB, fx *ltxFixture) {
method LTXFiles (line 894) | func (c *mockReplicaClient) LTXFiles(ctx context.Context, level int, s...
method OpenLTXFile (line 906) | func (c *mockReplicaClient) OpenLTXFile(ctx context.Context, level int...
method WriteLTXFile (line 924) | func (c *mockReplicaClient) WriteLTXFile(context.Context, int, ltx.TXI...
method DeleteLTXFiles (line 928) | func (c *mockReplicaClient) DeleteLTXFiles(context.Context, []*ltx.Fil...
method DeleteAll (line 932) | func (c *mockReplicaClient) DeleteAll(context.Context) error {
method key (line 966) | func (c *mockReplicaClient) key(info *ltx.FileInfo) string {
method makeKey (line 970) | func (c *mockReplicaClient) makeKey(level int, minTXID, maxTXID ltx.TX...
type blockingReplicaClient (line 836) | type blockingReplicaClient struct
method Type (line 936) | func (c *blockingReplicaClient) Type() string { return "blocking" }
method LTXFiles (line 938) | func (c *blockingReplicaClient) LTXFiles(ctx context.Context, level in...
method OpenLTXFile (line 950) | func (c *blockingReplicaClient) OpenLTXFile(ctx context.Context, level...
method WriteLTXFile (line 954) | func (c *blockingReplicaClient) WriteLTXFile(ctx context.Context, leve...
method DeleteLTXFiles (line 958) | func (c *blockingReplicaClient) DeleteLTXFiles(ctx context.Context, fi...
method DeleteAll (line 962) | func (c *blockingReplicaClient) DeleteAll(ctx context.Context) error {
type countingReplicaClient (line 844) | type countingReplicaClient struct
method Type (line 850) | func (c *countingReplicaClient) Type() string { return "count" }
method Init (line 852) | func (c *countingReplicaClient) Init(context.Context) error { return n...
method LTXFiles (line 854) | func (c *countingReplicaClient) LTXFiles(ctx context.Context, level in...
method OpenLTXFile (line 859) | func (c *countingReplicaClient) OpenLTXFile(context.Context, int, ltx....
method WriteLTXFile (line 863) | func (c *countingReplicaClient) WriteLTXFile(context.Context, int, ltx...
method DeleteLTXFiles (line 867) | func (c *countingReplicaClient) DeleteLTXFiles(context.Context, []*ltx...
method DeleteAll (line 869) | func (c *countingReplicaClient) DeleteAll(context.Context) error { ret...
function newCountingReplicaClient (line 848) | func newCountingReplicaClient() *countingReplicaClient { return &countin...
function newMockReplicaClient (line 871) | func newMockReplicaClient() *mockReplicaClient {
function newBlockingReplicaClient (line 875) | func newBlockingReplicaClient() *blockingReplicaClient {
type ltxFixture (line 974) | type ltxFixture struct
function buildLTXFixture (line 979) | func buildLTXFixture(tb testing.TB, txid ltx.TXID, fill byte) *ltxFixture {
function buildLTXFixtureWithPage (line 983) | func buildLTXFixtureWithPage(tb testing.TB, txid ltx.TXID, pageSize, pgn...
function buildLTXFixtureWithPages (line 987) | func buildLTXFixtureWithPages(tb testing.TB, txid ltx.TXID, pageSize uin...
function TestVFSFile_Hydration_Basic (line 1049) | func TestVFSFile_Hydration_Basic(t *testing.T) {
function TestVFSFile_Hydration_ReadsDuringHydration (line 1095) | func TestVFSFile_Hydration_ReadsDuringHydration(t *testing.T) {
function TestVFSFile_Hydration_CloseEarly (line 1125) | func TestVFSFile_Hydration_CloseEarly(t *testing.T) {
function TestVFSFile_Hydration_Disabled (line 1151) | func TestVFSFile_Hydration_Disabled(t *testing.T) {
function TestVFSFile_Hydration_IncrementalUpdates (line 1177) | func TestVFSFile_Hydration_IncrementalUpdates(t *testing.T) {
function TestHydrator_Close_Persistent (line 1221) | func TestHydrator_Close_Persistent(t *testing.T) {
function TestHydrator_Init_Resume (line 1251) | func TestHydrator_Init_Resume(t *testing.T) {
function TestHydrator_Close_TempFile (line 1283) | func TestHydrator_Close_TempFile(t *testing.T) {
function TestHydrator_Init_StaleMeta (line 1307) | func TestHydrator_Init_StaleMeta(t *testing.T) {
function TestVFSFile_Hydration_PersistentResumeOnReopen (line 1331) | func TestVFSFile_Hydration_PersistentResumeOnReopen(t *testing.T) {
FILE: vfs_write_test.go
type writeTestReplicaClient (line 25) | type writeTestReplicaClient struct
method Type (line 38) | func (c *writeTestReplicaClient) Type() string { return "test" }
method Init (line 40) | func (c *writeTestReplicaClient) Init(ctx context.Context) error { ret...
method LTXFiles (line 42) | func (c *writeTestReplicaClient) LTXFiles(ctx context.Context, level i...
method OpenLTXFile (line 55) | func (c *writeTestReplicaClient) OpenLTXFile(ctx context.Context, leve...
method WriteLTXFile (line 76) | func (c *writeTestReplicaClient) WriteLTXFile(ctx context.Context, lev...
method DeleteLTXFiles (line 100) | func (c *writeTestReplicaClient) DeleteLTXFiles(ctx context.Context, a...
method DeleteAll (line 104) | func (c *writeTestReplicaClient) DeleteAll(ctx context.Context) error {
function newWriteTestReplicaClient (line 31) | func newWriteTestReplicaClient() *writeTestReplicaClient {
function ltxKey (line 112) | func ltxKey(level int, minTXID, maxTXID ltx.TXID) string {
type writeTestFileIterator (line 117) | type writeTestFileIterator struct
method Next (line 122) | func (itr *writeTestFileIterator) Next() bool {
method Item (line 130) | func (itr *writeTestFileIterator) Item() *ltx.FileInfo {
method Close (line 137) | func (itr *writeTestFileIterator) Close() error {
method Err (line 141) | func (itr *writeTestFileIterator) Err() error {
function createTestLTXFile (line 146) | func createTestLTXFile(t *testing.T, client *writeTestReplicaClient, txi...
function setupWriteableVFSFile (line 198) | func setupWriteableVFSFile(t *testing.T, client *writeTestReplicaClient)...
function TestVFSFile_WriteEnabled (line 226) | func TestVFSFile_WriteEnabled(t *testing.T) {
function TestVFSFile_WriteAt (line 260) | func TestVFSFile_WriteAt(t *testing.T) {
function TestVFSFile_SyncToRemote (line 306) | func TestVFSFile_SyncToRemote(t *testing.T) {
function TestVFSFile_ConflictDetection (line 351) | func TestVFSFile_ConflictDetection(t *testing.T) {
function TestVFSFile_TransactionTracking (line 385) | func TestVFSFile_TransactionTracking(t *testing.T) {
function TestVFSFile_Truncate (line 441) | func TestVFSFile_Truncate(t *testing.T) {
function TestVFSFile_WriteBuffer (line 479) | func TestVFSFile_WriteBuffer(t *testing.T) {
function TestVFSFile_WriteBufferDiscardedOnOpen (line 536) | func TestVFSFile_WriteBufferDiscardedOnOpen(t *testing.T) {
function TestVFSFile_WriteBufferClearAfterSync (line 602) | func TestVFSFile_WriteBufferClearAfterSync(t *testing.T) {
function TestVFSFile_OpenFailsWithInvalidBufferPath (line 650) | func TestVFSFile_OpenFailsWithInvalidBufferPath(t *testing.T) {
function TestVFSFile_BufferFileAlwaysCreatedWhenWriteEnabled (line 671) | func TestVFSFile_BufferFileAlwaysCreatedWhenWriteEnabled(t *testing.T) {
function TestVFSFile_OpenNewDatabase (line 698) | func TestVFSFile_OpenNewDatabase(t *testing.T) {
function TestVFSFile_NewDatabase_ReadReturnsZeros (line 742) | func TestVFSFile_NewDatabase_ReadReturnsZeros(t *testing.T) {
function TestVFSFile_NewDatabase_WriteAndSync (line 780) | func TestVFSFile_NewDatabase_WriteAndSync(t *testing.T) {
function TestVFSFile_NewDatabase_FileSize (line 841) | func TestVFSFile_NewDatabase_FileSize(t *testing.T) {
function TestSetWriteEnabled_ReadValue (line 885) | func TestSetWriteEnabled_ReadValue(t *testing.T) {
function TestSetWriteEnabled_ReadValueEnabled (line 912) | func TestSetWriteEnabled_ReadValueEnabled(t *testing.T) {
function TestSetWriteEnabled_DisableSyncsDirtyPages (line 937) | func TestSetWriteEnabled_DisableSyncsDirtyPages(t *testing.T) {
function TestSetWriteEnabled_DisableWaitsForTransaction (line 984) | func TestSetWriteEnabled_DisableWaitsForTransaction(t *testing.T) {
function TestSetWriteEnabled_EnableAfterDisable (line 1061) | func TestSetWriteEnabled_EnableAfterDisable(t *testing.T) {
function TestSetWriteEnabled_DisableWithTimeout (line 1104) | func TestSetWriteEnabled_DisableWithTimeout(t *testing.T) {
function TestSetWriteEnabled_ColdEnable (line 1157) | func TestSetWriteEnabled_ColdEnable(t *testing.T) {
function TestSetWriteEnabled_NoOpWhenAlreadyInState (line 1216) | func TestSetWriteEnabled_NoOpWhenAlreadyInState(t *testing.T) {
function TestSetWriteEnabled_FileControlWrite (line 1254) | func TestSetWriteEnabled_FileControlWrite(t *testing.T) {
function TestSetWriteEnabled_InvalidValue (line 1328) | func TestSetWriteEnabled_InvalidValue(t *testing.T) {
type failingWriteClient (line 1354) | type failingWriteClient struct
method WriteLTXFile (line 1367) | func (c *failingWriteClient) WriteLTXFile(ctx context.Context, level i...
function newFailingWriteClient (line 1360) | func newFailingWriteClient(failAfter int) *failingWriteClient {
function TestSetWriteEnabled_SyncFailureKeepsWritesEnabled (line 1379) | func TestSetWriteEnabled_SyncFailureKeepsWritesEnabled(t *testing.T) {
function TestSetWriteEnabled_DisablingPreventsNewTransactions (line 1444) | func TestSetWriteEnabled_DisablingPreventsNewTransactions(t *testing.T) {
function TestSetWriteEnabled_ConcurrentEnableDisable (line 1517) | func TestSetWriteEnabled_ConcurrentEnableDisable(t *testing.T) {
function TestLock_BlocksDuringDisable (line 1573) | func TestLock_BlocksDuringDisable(t *testing.T) {
function TestLock_BlocksDuringDisable_MultipleWaiters (line 1667) | func TestLock_BlocksDuringDisable_MultipleWaiters(t *testing.T) {
function openWriteVFSFile (line 1774) | func openWriteVFSFile(t *testing.T, vfs *VFS) *VFSFile {
function TestVFS_MultipleConnections_NoFalseConflict (line 1785) | func TestVFS_MultipleConnections_NoFalseConflict(t *testing.T) {
function TestVFS_WriteLockBlocksConcurrentWriters (line 1836) | func TestVFS_WriteLockBlocksConcurrentWriters(t *testing.T) {
function TestVFS_ConcurrentOpenAllSucceed (line 1873) | func TestVFS_ConcurrentOpenAllSucceed(t *testing.T) {
function TestVFS_UniqueBufferPaths (line 1912) | func TestVFS_UniqueBufferPaths(t *testing.T) {
function TestVFS_RealConflict_StillDetected (line 1929) | func TestVFS_RealConflict_StillDetected(t *testing.T) {
function TestVFS_CloseReleasesWriteSlot (line 1959) | func TestVFS_CloseReleasesWriteSlot(t *testing.T) {
FILE: wal_reader.go
type WALReader (line 19) | type WALReader struct
method PageSize (line 78) | func (r *WALReader) PageSize() uint32 { return r.pageSize }
method Offset (line 82) | func (r *WALReader) Offset() int64 {
method readHeader (line 90) | func (r *WALReader) readHeader() error {
method ReadFrame (line 133) | func (r *WALReader) ReadFrame(ctx context.Context, data []byte) (pgno,...
method readFrame (line 137) | func (r *WALReader) readFrame(_ context.Context, data []byte, verifyCh...
method PageMap (line 192) | func (r *WALReader) PageMap(ctx context.Context) (m map[uint32]int64, ...
method FrameSaltsUntil (line 247) | func (r *WALReader) FrameSaltsUntil(ctx context.Context, until [2]uint...
function NewWALReader (line 34) | func NewWALReader(rd io.ReaderAt, logger *slog.Logger) (*WALReader, erro...
function NewWALReaderWithOffset (line 45) | func NewWALReaderWithOffset(ctx context.Context, rd io.ReaderAt, offset ...
function WALChecksum (line 273) | func WALChecksum(bo binary.ByteOrder, s0, s1 uint32, b []byte) (uint32, ...
type PrevFrameMismatchError (line 284) | type PrevFrameMismatchError struct
method Error (line 288) | func (e *PrevFrameMismatchError) Error() string {
method Unwrap (line 292) | func (e *PrevFrameMismatchError) Unwrap() error {
FILE: wal_reader_test.go
function TestWALReader (line 15) | func TestWALReader(t *testing.T) {
function TestWALReader_FrameSaltsUntil (line 249) | func TestWALReader_FrameSaltsUntil(t *testing.T) {
FILE: webdav/replica_client.go
function init (line 23) | func init() {
constant ReplicaClientType (line 28) | ReplicaClientType = "webdav"
constant DefaultTimeout (line 31) | DefaultTimeout = 30 * time.Second
type ReplicaClient (line 36) | type ReplicaClient struct
method SetLogger (line 55) | func (c *ReplicaClient) SetLogger(logger *slog.Logger) {
method Type (line 87) | func (c *ReplicaClient) Type() string {
method Init (line 91) | func (c *ReplicaClient) Init(ctx context.Context) error {
method init (line 97) | func (c *ReplicaClient) init(ctx context.Context) (_ *gowebdav.Client,...
method DeleteAll (line 121) | func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
method LTXFiles (line 136) | func (c *ReplicaClient) LTXFiles(ctx context.Context, level int, seek ...
method WriteLTXFile (line 236) | func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, m...
method OpenLTXFile (line 301) | func (c *ReplicaClient) OpenLTXFile(ctx context.Context, level int, mi...
method DeleteLTXFiles (line 353) | func (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.F...
function NewReplicaClient (line 48) | func NewReplicaClient() *ReplicaClient {
function NewReplicaClientFromURL (line 62) | func NewReplicaClientFromURL(scheme, host, urlPath string, query url.Val...
FILE: webdav/replica_client_test.go
function TestReplicaClient_Type (line 21) | func TestReplicaClient_Type(t *testing.T) {
function TestReplicaClient_Init_RequiresURL (line 28) | func TestReplicaClient_Init_RequiresURL(t *testing.T) {
function TestReplicaClient_DeleteAll_NotFound (line 39) | func TestReplicaClient_DeleteAll_NotFound(t *testing.T) {
function TestReplicaClient_LTXFiles_PathNotFound (line 54) | func TestReplicaClient_LTXFiles_PathNotFound(t *testing.T) {
function TestReplicaClient_OpenLTXFile_RangeFallback (line 75) | func TestReplicaClient_OpenLTXFile_RangeFallback(t *testing.T) {
function TestReplicaClient_OpenLTXFile_OffsetOnly (line 108) | func TestReplicaClient_OpenLTXFile_OffsetOnly(t *testing.T) {
function newTestReplicaClient (line 142) | func newTestReplicaClient(baseURL string) *webdav.ReplicaClient {
function buildLTXPayload (line 150) | func buildLTXPayload(minTXID, maxTXID ltx.TXID, payload []byte) []byte {
type fakeWebDAVServer (line 164) | type fakeWebDAVServer struct
method ServeHTTP (line 180) | func (s *fakeWebDAVServer) ServeHTTP(w http.ResponseWriter, r *http.Re...
method handleGet (line 204) | func (s *fakeWebDAVServer) handleGet(w http.ResponseWriter, r *http.Re...
method handleDelete (line 229) | func (s *fakeWebDAVServer) handleDelete(w http.ResponseWriter, r *http...
method handlePropfind (line 245) | func (s *fakeWebDAVServer) handlePropfind(w http.ResponseWriter, r *ht...
function newFakeWebDAVServer (line 172) | func newFakeWebDAVServer() *fakeWebDAVServer {
function parseRange (line 273) | func parseRange(header string, size int) (start, end int, err error) {
Condensed preview — 265 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,431K chars).
[
{
"path": ".aiexclude",
"chars": 666,
"preview": "# .aiexclude - Files to exclude from Gemini Code Assist\n# This file works like .gitignore for AI context\n\n# Sensitive fi"
},
{
"path": ".claude/agents/ltx-compaction-specialist.md",
"chars": 4113,
"preview": "---\nrole: LTX Format and Compaction Specialist\ntools:\n - read\n - write\n - edit\n - grep\n - bash\npriority: high\n---\n\n"
},
{
"path": ".claude/agents/performance-optimizer.md",
"chars": 4803,
"preview": "---\nrole: Performance Optimizer\ntools:\n - read\n - write\n - edit\n - bash\n - grep\npriority: medium\n---\n\n# Performance"
},
{
"path": ".claude/agents/replica-client-developer.md",
"chars": 4353,
"preview": "---\nrole: Replica Client Developer\ntools:\n - read\n - write\n - edit\n - grep\n - bash\npriority: high\n---\n\n# Replica Cl"
},
{
"path": ".claude/agents/sqlite-expert.md",
"chars": 2113,
"preview": "---\nrole: SQLite WAL and Page Expert\ntools:\n - read\n - write\n - edit\n - grep\n - bash\npriority: high\n---\n\n# SQLite E"
},
{
"path": ".claude/agents/test-engineer.md",
"chars": 4105,
"preview": "---\nrole: Test Engineer\ntools:\n - read\n - write\n - edit\n - bash\n - grep\npriority: medium\n---\n\n# Test Engineer Agent"
},
{
"path": ".claude/commands/add-storage-backend.md",
"chars": 2516,
"preview": "---\ndescription: Create a new storage backend implementation\n---\n\n# Add Storage Backend Command\n\nCreate a new storage ba"
},
{
"path": ".claude/commands/analyze-ltx.md",
"chars": 2229,
"preview": "Analyze LTX file issues in Litestream. This command helps diagnose problems with LTX files, including corruption, missin"
},
{
"path": ".claude/commands/debug-ipc.md",
"chars": 4168,
"preview": "---\ndescription: Debug IPC Unix socket issues\n---\n\n# Debug IPC Command\n\nDiagnose issues with the Litestream IPC control "
},
{
"path": ".claude/commands/debug-wal.md",
"chars": 2532,
"preview": "Debug WAL monitoring issues in Litestream. This command helps diagnose problems with WAL change detection, checkpointing"
},
{
"path": ".claude/commands/fix-common-issues.md",
"chars": 7266,
"preview": "---\ndescription: Fix common Litestream issues\n---\n\n# Fix Common Issues Command\n\nDiagnose and fix common issues in Litest"
},
{
"path": ".claude/commands/run-comprehensive-tests.md",
"chars": 5132,
"preview": "---\ndescription: Run comprehensive test suite for Litestream\n---\n\n# Run Comprehensive Tests Command\n\nExecute a full test"
},
{
"path": ".claude/commands/test-compaction.md",
"chars": 1717,
"preview": "Test Litestream compaction logic. This command helps test and debug compaction issues, especially with eventually consis"
},
{
"path": ".claude/commands/trace-replication.md",
"chars": 3488,
"preview": "Trace the complete replication flow in Litestream. This command helps understand how changes flow from SQLite through to"
},
{
"path": ".claude/commands/validate-replica.md",
"chars": 2093,
"preview": "Validate a ReplicaClient implementation in Litestream. This command helps ensure a replica client correctly implements t"
},
{
"path": ".claude/settings.json",
"chars": 367,
"preview": "{\n \"project_name\": \"Litestream\",\n \"description\": \"Standalone disaster recovery tool for SQLite\",\n\n \"auto_formatting\":"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1541,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve Litestream\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n<!--"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 323,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Documentation Issues\n url: https://github.com/benbjohnson/litestr"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 771,
"preview": "---\nname: Feature request\nabout: Suggest an idea for Litestream\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n<!--- "
},
{
"path": ".github/pull_request_template.md",
"chars": 926,
"preview": "## Description\n<!--- Describe your changes in detail -->\n\n## Motivation and Context\n<!--- Why is this change required? W"
},
{
"path": ".github/workflows/commit.yml",
"chars": 18441,
"preview": "on:\n push:\n branches:\n - main\n pull_request:\n types:\n - opened\n - synchronize\n - reopened\n\nn"
},
{
"path": ".github/workflows/integration-tests.yml",
"chars": 4712,
"preview": "name: Integration Tests\n\non:\n pull_request:\n paths:\n - '**.go'\n - 'go.mod'\n - 'go.sum'\n - 'tests"
},
{
"path": ".github/workflows/manual-integration-tests.yml",
"chars": 25285,
"preview": "name: Manual Integration Tests\n\npermissions:\n issues: write\n pull-requests: write\n\non:\n workflow_dispatch:\n inputs"
},
{
"path": ".github/workflows/pr-metrics.yml",
"chars": 16675,
"preview": "name: PR Build Metrics\n\non:\n pull_request:\n types: [opened, synchronize, reopened]\n\nconcurrency:\n group: pr-metrics"
},
{
"path": ".github/workflows/pre-release-checklist.yml",
"chars": 18065,
"preview": "name: Pre-Release Checklist\n\n# Advisory workflow to verify release readiness\n# This workflow reports results but does NO"
},
{
"path": ".github/workflows/release.docker.yml",
"chars": 2352,
"preview": "on:\n release:\n types:\n - published\n# pull_request:\n# types:\n# - opened\n# - synchronize\n# - reopen"
},
{
"path": ".github/workflows/release.yml",
"chars": 17085,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n workflow_dispatch:\n inputs:\n tag:\n description: 'Rele"
},
{
"path": ".github/workflows/stale-issues.yml",
"chars": 1480,
"preview": "name: Stale Issue Manager\n\non:\n schedule:\n - cron: '0 0 * * *'\n workflow_dispatch:\n\npermissions:\n issues: write\n\nj"
},
{
"path": ".github/workflows/upgrade-tests.yml",
"chars": 1485,
"preview": "name: Upgrade Tests\n\non:\n pull_request:\n paths:\n - '**.go'\n - 'go.mod'\n - 'go.sum'\n - 'tests/int"
},
{
"path": ".gitignore",
"chars": 544,
"preview": ".DS_Store\n/src/litestream-vfs.h\n/dist\n.vscode\n.sprite\n\n# Claude-related files (force include despite global gitignore)\n!"
},
{
"path": ".goreleaser.yml",
"chars": 4630,
"preview": "version: 2\n\nproject_name: litestream\n\nbefore:\n hooks:\n - go mod tidy\n\nbuilds:\n - id: litestream\n main: ./cmd/lit"
},
{
"path": ".markdownlint.json",
"chars": 200,
"preview": "{\n \"default\": true,\n \"MD013\": false,\n \"MD024\": false,\n \"MD026\": false,\n \"MD031\": false,\n \"MD032\": false,\n \"MD033\""
},
{
"path": ".pre-commit-config.yaml",
"chars": 530,
"preview": "repos:\n - repo: https://github.com/pre-commit/pre-commit-hooks\n rev: v4.1.0\n hooks:\n - id: trailing-whitespa"
},
{
"path": "AGENTS.md",
"chars": 3922,
"preview": "# AGENTS.md - Litestream AI Agent Guide\n\nLitestream is a disaster recovery tool for SQLite that runs as a background pro"
},
{
"path": "AI_PR_GUIDE.md",
"chars": 4247,
"preview": "# AI-Assisted Contribution Guide\n\nThis guide helps AI assistants (and humans using them) submit high-quality PRs to Lite"
},
{
"path": "CONTRIBUTING.md",
"chars": 5238,
"preview": "# Contributing to Litestream\n\nThank you for your interest in contributing to Litestream! We value community contribution"
},
{
"path": "Dockerfile",
"chars": 1996,
"preview": "FROM golang:1.25 AS builder\n\n# Install build dependencies for VFS extension\nRUN apt-get update && apt-get install -y gcc"
},
{
"path": "GEMINI.md",
"chars": 1331,
"preview": "# GEMINI.md - Gemini Code Assist Configuration\n\nGemini-specific configuration for Litestream. See [AGENTS.md](AGENTS.md)"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 2831,
"preview": "default:\n\ndocker:\n\tdocker build -t litestream .\n\n# VFS build configuration\nVFS_BUILD_TAGS := vfs,SQLITE3VFS_LOADABLE_EXT"
},
{
"path": "README.md",
"chars": 2475,
"preview": "Litestream\n\n\n\n// DatabasesCommand is a command for listing"
},
{
"path": "cmd/litestream/directory_watcher.go",
"chars": 9884,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/"
},
{
"path": "cmd/litestream/directory_watcher_stress_test.go",
"chars": 7282,
"preview": "//go:build stress\n\npackage main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/a"
},
{
"path": "cmd/litestream/directory_watcher_test.go",
"chars": 3581,
"preview": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/fsnotify/fsnotify\"\n)\n\nfunc TestDirectoryMonitor_shouldSkipPath(t *testin"
},
{
"path": "cmd/litestream/info.go",
"chars": 2782,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjoh"
},
{
"path": "cmd/litestream/info_test.go",
"chars": 3512,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\tmai"
},
{
"path": "cmd/litestream/list.go",
"chars": 2766,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/benbjoh"
},
{
"path": "cmd/litestream/list_test.go",
"chars": 3805,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\tmain \"github.com/benbjohnson/li"
},
{
"path": "cmd/litestream/ltx.go",
"chars": 3417,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"text/tabwriter\"\n\t\"time\"\n\n\t\"github.com/benbjohnson/litestream\"\n)"
},
{
"path": "cmd/litestream/ltx_test.go",
"chars": 3577,
"preview": "package main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\nfunc TestTXIDVarP"
},
{
"path": "cmd/litestream/main.go",
"chars": 59233,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"math\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\""
},
{
"path": "cmd/litestream/main_notwindows.go",
"chars": 440,
"preview": "//go:build !windows\n\npackage main\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n)\n\nconst defaultConfigPath = \"/etc/"
},
{
"path": "cmd/litestream/main_test.go",
"chars": 90736,
"preview": "package main_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\""
},
{
"path": "cmd/litestream/main_windows.go",
"chars": 3010,
"preview": "//go:build windows\n\npackage main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"os/signal\"\n\n\t\"golang.org/x/sys/windows\"\n"
},
{
"path": "cmd/litestream/mcp.go",
"chars": 12226,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/register.go",
"chars": 2993,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/register_test.go",
"chars": 4165,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\tmain \"github.c"
},
{
"path": "cmd/litestream/replicate.go",
"chars": 17784,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"os\"\n\t\"os"
},
{
"path": "cmd/litestream/replicate_test.go",
"chars": 9715,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\tmain \"github.com/benbjohnson/litestream/cmd/litestr"
},
{
"path": "cmd/litestream/reset.go",
"chars": 3311,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n\n// Rese"
},
{
"path": "cmd/litestream/restore.go",
"chars": 7354,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/benbjohnso"
},
{
"path": "cmd/litestream/restore_test.go",
"chars": 1347,
"preview": "package main\n\nimport (\n\t\"flag\"\n\t\"testing\"\n\t\"time\"\n\n\tlitestream \"github.com/benbjohnson/litestream\"\n)\n\nfunc TestRestoreCo"
},
{
"path": "cmd/litestream/start.go",
"chars": 2626,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/status.go",
"chars": 3295,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"text/tabwriter\"\n\n\t\"github.com/dustin/go-humanize\"\n\n\t\"github.com"
},
{
"path": "cmd/litestream/status_test.go",
"chars": 1783,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\tmain \"github.com/benbjohnson/litestream/cmd/l"
},
{
"path": "cmd/litestream/stop.go",
"chars": 2658,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/sync.go",
"chars": 2818,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/sync_test.go",
"chars": 3179,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\tmain \"github.com/benbjohnson/li"
},
{
"path": "cmd/litestream/unregister.go",
"chars": 2673,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "cmd/litestream/unregister_test.go",
"chars": 3677,
"preview": "package main_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\tmain \"github.com/benbjohnson/li"
},
{
"path": "cmd/litestream/version.go",
"chars": 590,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n)\n\n// VersionCommand represents a command to print the current version."
},
{
"path": "cmd/litestream-test/README.md",
"chars": 6405,
"preview": "# litestream-test\n\nA CLI testing harness for Litestream that provides tools for database population, load generation, an"
},
{
"path": "cmd/litestream-test/S3-RETENTION-TESTING.md",
"chars": 10196,
"preview": "# S3 LTX File Retention Testing Guide\n\n## Overview\n\nThis document describes the comprehensive S3 LTX file retention test"
},
{
"path": "cmd/litestream-test/load.go",
"chars": 7788,
"preview": "package main\n\nimport (\n\t\"context\"\n\tcryptorand \"crypto/rand\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"math/rand\"\n\t\"os"
},
{
"path": "cmd/litestream-test/main.go",
"chars": 2525,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nvar (\n\tVersion = \"deve"
},
{
"path": "cmd/litestream-test/populate.go",
"chars": 7805,
"preview": "package main\n\nimport (\n\t\"context\"\n\tcryptorand \"crypto/rand\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"math/rand\"\n\t\"os"
},
{
"path": "cmd/litestream-test/scripts/README.md",
"chars": 11254,
"preview": "# Litestream Test Scripts\n\nComprehensive test scripts for validating Litestream functionality across various scenarios. "
},
{
"path": "cmd/litestream-test/scripts/reproduce-critical-bug.sh",
"chars": 6566,
"preview": "#!/bin/bash\n\n# Litestream v0.5.0 Critical Bug Reproduction Script\n#\n# This script demonstrates a CRITICAL data loss bug "
},
{
"path": "cmd/litestream-test/scripts/test-754-restore-focus.sh",
"chars": 8386,
"preview": "#!/bin/bash\nset -e\n\n# Aggressive #754 reproduction - focus on RESTORE scenarios\n# The \"ltx verification failed\" error mo"
},
{
"path": "cmd/litestream-test/scripts/test-754-s3-scenarios.sh",
"chars": 12754,
"preview": "#!/bin/bash\nset -e\n\n# Test #754 flag issue with S3 scenarios and retention cleanup\n# Tests both S3 vs file replication b"
},
{
"path": "cmd/litestream-test/scripts/test-format-isolation.sh",
"chars": 9783,
"preview": "#!/bin/bash\nset -e\n\n# Test to verify whether v0.5.0 can actually restore from PURE v0.3.x files\n# Or if it's creating ne"
},
{
"path": "cmd/litestream-test/scripts/test-massive-upgrade.sh",
"chars": 11012,
"preview": "#!/bin/bash\nset -e\n\n# Massive database upgrade test - extreme stress testing\n# Create large DB with lots of snapshots an"
},
{
"path": "cmd/litestream-test/scripts/test-quick-format-check.sh",
"chars": 1368,
"preview": "#!/bin/bash\nset -e\n\n# Quick test: Can v0.5.0 restore from PURE v0.3.x files?\n\necho \"Quick Format Compatibility Test\"\nech"
},
{
"path": "cmd/litestream-test/scripts/test-s3-access-point.sh",
"chars": 7113,
"preview": "#!/bin/bash\nset -e\n\n# Test S3 Access Point ARN support (Issue #923)\n# This script tests that Litestream can replicate to"
},
{
"path": "cmd/litestream-test/scripts/test-s3-retention-cleanup.sh",
"chars": 7896,
"preview": "#!/bin/bash\nset -e\n\n# Test S3 LTX file retention and cleanup behavior\n# This script helps verify that old LTX files are "
},
{
"path": "cmd/litestream-test/scripts/test-s3-retention-comprehensive.sh",
"chars": 17123,
"preview": "#!/bin/bash\nset -e\n\n# Comprehensive S3 LTX file retention testing script\n# Tests both small and large databases with var"
},
{
"path": "cmd/litestream-test/scripts/test-s3-retention-large-db.sh",
"chars": 17551,
"preview": "#!/bin/bash\nset -e\n\n# Test S3 LTX file retention cleanup with large databases (>1GB) using local S3 mock\n# This script s"
},
{
"path": "cmd/litestream-test/scripts/test-s3-retention-small-db.sh",
"chars": 11843,
"preview": "#!/bin/bash\nset -e\n\n# Test S3 LTX file retention cleanup with small databases using local S3 mock\n# This script tests th"
},
{
"path": "cmd/litestream-test/scripts/test-simple-754-reproduction.sh",
"chars": 5550,
"preview": "#!/bin/bash\nset -e\n\n# Simple, direct test to reproduce #754 flag issue\n# Focus on the core HeaderFlagNoChecksum problem\n"
},
{
"path": "cmd/litestream-test/scripts/test-upgrade-large-db.sh",
"chars": 6832,
"preview": "#!/bin/bash\nset -e\n\n# Test Litestream v0.3.x to v0.5.0 upgrade with large database (>1GB)\n# Specifically testing for #75"
},
{
"path": "cmd/litestream-test/scripts/test-upgrade-v0.3-to-v0.5.sh",
"chars": 10032,
"preview": "#!/bin/bash\nset -e\n\n# Test Litestream v0.3.x to v0.5.0 upgrade scenarios\n# Based on conversation with Ben Johnson about "
},
{
"path": "cmd/litestream-test/scripts/test-v0.5-flag-reproduction.sh",
"chars": 7407,
"preview": "#!/bin/bash\nset -e\n\n# Test to reproduce original #754 flag issue\n# This recreates the scenario where #754 was first disc"
},
{
"path": "cmd/litestream-test/scripts/test-v0.5-restart-scenarios.sh",
"chars": 8659,
"preview": "#!/bin/bash\nset -e\n\n# Test v0.5.0 restart scenarios to reproduce #754 flag issue\n# Focus on HeaderFlagNoChecksum usage a"
},
{
"path": "cmd/litestream-test/scripts/verify-test-setup.sh",
"chars": 3461,
"preview": "#!/bin/bash\n\n# Script to verify test environment is set up correctly\n# Ensures we're using local builds, not system-inst"
},
{
"path": "cmd/litestream-test/shrink.go",
"chars": 7667,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqli"
},
{
"path": "cmd/litestream-test/validate.go",
"chars": 12576,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"os/exec\"\n\t\"stri"
},
{
"path": "cmd/litestream-vfs/chaos_test.go",
"chars": 5696,
"preview": "//go:build vfs && chaos\n// +build vfs,chaos\n\npackage main_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"math/rand\"\n\t\"sync/a"
},
{
"path": "cmd/litestream-vfs/fuzz_test.go",
"chars": 4636,
"preview": "//go:build vfs\n// +build vfs\n\npackage main_test\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\""
},
{
"path": "cmd/litestream-vfs/hydration_e2e_test.go",
"chars": 10112,
"preview": "//go:build vfs\n// +build vfs\n\npackage main_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\""
},
{
"path": "cmd/litestream-vfs/main.go",
"chars": 4680,
"preview": "//go:build SQLITE3VFS_LOADABLE_EXT\n// +build SQLITE3VFS_LOADABLE_EXT\n\npackage main\n\n// import C is necessary export to t"
},
{
"path": "cmd/litestream-vfs/main_test.go",
"chars": 72413,
"preview": "//go:build vfs\n// +build vfs\n\npackage main_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"l"
},
{
"path": "cmd/litestream-vfs/stress_test.go",
"chars": 2269,
"preview": "//go:build vfs && stress\n// +build vfs,stress\n\npackage main_test\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"os\"\n\t\"runtime\"\n\t\"sy"
},
{
"path": "cmd/litestream-vfs/time_travel_test.go",
"chars": 10621,
"preview": "//go:build vfs\n// +build vfs\n\npackage main_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n"
},
{
"path": "cmd/litestream-vfs/vfs_soak_test.go",
"chars": 3509,
"preview": "//go:build vfs && soak\n// +build vfs,soak\n\npackage main_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\""
},
{
"path": "cmd/litestream-vfs/vfs_write_integration_test.go",
"chars": 45408,
"preview": "//go:build vfs\n// +build vfs\n\npackage main_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t"
},
{
"path": "compaction_level.go",
"chars": 3545,
"preview": "package litestream\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n// SnapshotLevel represents the level which full snapshots are held.\ncons"
},
{
"path": "compactor.go",
"chars": 11832,
"preview": "package litestream\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/prometheus/client_golang/pr"
},
{
"path": "compactor_test.go",
"chars": 16864,
"preview": "package litestream_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"log/slog\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n\n"
},
{
"path": "db.go",
"chars": 79005,
"preview": "package litestream\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/crc64\"\n\t\"io\""
},
{
"path": "db_internal_test.go",
"chars": 48510,
"preview": "package litestream\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t"
},
{
"path": "db_shutdown_test.go",
"chars": 11765,
"preview": "package litestream_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/"
},
{
"path": "db_test.go",
"chars": 49185,
"preview": "package litestream_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"hash/crc64\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t"
},
{
"path": "docker-compose.test.yml",
"chars": 356,
"preview": "services:\n minio:\n image: minio/minio:latest\n ports:\n - \"9000:9000\"\n - \"9001:9001\"\n environment:\n "
},
{
"path": "docs/ARCHITECTURE.md",
"chars": 22309,
"preview": "# Litestream Architecture - Technical Deep Dive\n\n## Table of Contents\n- [System Layers](#system-layers)\n- [Core Componen"
},
{
"path": "docs/DOC_MAINTENANCE.md",
"chars": 7143,
"preview": "# Documentation Maintenance Guide\n\nThis guide ensures documentation stays synchronized with code changes and follows the"
},
{
"path": "docs/LTX_FORMAT.md",
"chars": 15798,
"preview": "# LTX Format Specification\n\nLTX (Log Transaction) is Litestream's custom format for storing database changes in an immut"
},
{
"path": "docs/PATTERNS.md",
"chars": 16376,
"preview": "# Litestream Code Patterns and Anti-Patterns\n\nThis document contains detailed code patterns, examples, and anti-patterns"
},
{
"path": "docs/PENDING_USER_DOCS.md",
"chars": 1858,
"preview": "# Pending User-Facing Documentation\n\nThis file tracks open issues on [benbjohnson/litestream.io](https://github.com/benb"
},
{
"path": "docs/PROVIDER_COMPATIBILITY.md",
"chars": 11698,
"preview": "# Storage Provider Compatibility Guide\n\nThis document details S3-compatible storage provider compatibility with Litestre"
},
{
"path": "docs/REPLICA_CLIENT_GUIDE.md",
"chars": 22161,
"preview": "# ReplicaClient Implementation Guide\n\nThis guide provides comprehensive instructions for implementing new storage backen"
},
{
"path": "docs/SQLITE_INTERNALS.md",
"chars": 13779,
"preview": "# SQLite Internals for Litestream\n\nThis document explains SQLite internals critical for understanding Litestream's opera"
},
{
"path": "docs/TESTING_GUIDE.md",
"chars": 23861,
"preview": "# Litestream Testing Guide\n\nComprehensive guide for testing Litestream components and handling edge cases.\n\n## Table of "
},
{
"path": "docs/VFS.md",
"chars": 9463,
"preview": "# Litestream VFS\n\nThe Litestream VFS (Virtual File System) is a SQLite extension that allows applications to read direct"
},
{
"path": "etc/build.ps1",
"chars": 467,
"preview": "[CmdletBinding()]\nParam (\n [Parameter(Mandatory = $true)]\n [String] $Version\n)\n$ErrorActionPreference = \"Stop\"\n\n# "
},
{
"path": "etc/gon-sign.hcl",
"chars": 448,
"preview": "source = [\"./dist/litestream\"]\nbundle_id = \"com.middlemost.litestream\"\n\napple_id {\n username = \"@env:APPLE_ID_USERNAME\""
},
{
"path": "etc/gon.hcl",
"chars": 288,
"preview": "source = [\"./dist/litestream\"]\nbundle_id = \"com.middlemost.litestream\"\n\napple_id {\n username = \"benbjohnson@yahoo.com\"\n"
},
{
"path": "etc/litestream.service",
"chars": 134,
"preview": "[Unit]\nDescription=Litestream\n\n[Service]\nRestart=always\nExecStart=/usr/bin/litestream replicate\n\n[Install]\nWantedBy=mult"
},
{
"path": "etc/litestream.wxs",
"chars": 2211,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Wix\n\txmlns=\"http://schemas.microsoft.com/wix/2006/wi\"\n\txmlns:util=\"http://schema"
},
{
"path": "etc/litestream.yml",
"chars": 1665,
"preview": "# AWS credentials\n# access-key-id: AKIAxxxxxxxxxxxxxxxx\n# secret-access-key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxx"
},
{
"path": "etc/nfpm.yml",
"chars": 546,
"preview": "name: litestream\narch: \"${GOARCH}\"\nplatform: \"${GOOS}\"\nversion: \"${LITESTREAM_VERSION}\"\nsection: \"default\"\npriority: \"ex"
},
{
"path": "etc/run-s3-docker-tests.sh",
"chars": 1798,
"preview": "#!/bin/bash\nset -e\n\n# Script to run S3 integration tests against a local MinIO container.\n# This provides a more realist"
},
{
"path": "etc/s3_mock.py",
"chars": 914,
"preview": "#!/usr/bin/env python3\nimport sys\nimport os\nimport time\nfrom moto.server import ThreadedMotoServer\nimport boto3\nimport s"
},
{
"path": "file/replica_client.go",
"chars": 10809,
"preview": "package file\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"time\"\n"
},
{
"path": "file/replica_client_test.go",
"chars": 20578,
"preview": "package file_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"git"
},
{
"path": "go.mod",
"chars": 6336,
"preview": "module github.com/benbjohnson/litestream\n\ngo 1.25.0\n\ntoolchain go1.25.8\n\nrequire (\n\tcloud.google.com/go/storage v1.36.0\n"
},
{
"path": "go.sum",
"chars": 42390,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.111.0 h1:YHLKN"
},
{
"path": "grafana/README.md",
"chars": 1660,
"preview": "# Litestream Grafana Dashboard\n\nThis directory contains a Grafana dashboard for monitoring Litestream metrics.\n\n## Prere"
},
{
"path": "grafana/litestream-dashboard.json",
"chars": 20697,
"preview": "{\n \"annotations\": {\n \"list\": [\n {\n \"builtIn\": 1,\n \"datasource\": \"-- Grafana --\",\n \"enable\""
},
{
"path": "gs/replica_client.go",
"chars": 8589,
"preview": "package gs\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"time\"\n\n\t\""
},
{
"path": "gs/replica_client_test.go",
"chars": 1721,
"preview": "package gs\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/fsouza/fake-gcs-server/fakestorage\"\n\t\"gi"
},
{
"path": "heartbeat.go",
"chars": 1575,
"preview": "package litestream\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tDefaultHeartbeatInterval = 5 * ti"
},
{
"path": "heartbeat_test.go",
"chars": 7759,
"preview": "package litestream_test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"path/filepath\"\n\t\"sync/atomic\"\n\t\"testing\""
},
{
"path": "internal/hexdump.go",
"chars": 1259,
"preview": "package internal\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\nfunc Hexdump(data []byte) string {\n\tprevRow := make([]byte, 16)\n\n\tvar buf "
},
{
"path": "internal/internal.go",
"chars": 5040,
"preview": "package internal\n\nimport (\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"syscall\"\n\n\t\"github.com/pierrec/lz4/v4\"\n\t\"github.com/prometheus/clie"
},
{
"path": "internal/internal_unix.go",
"chars": 509,
"preview": "//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n// +build aix darwin dragonfly"
},
{
"path": "internal/internal_windows.go",
"chars": 504,
"preview": "//go:build windows\n// +build windows\n\npackage internal\n\nimport (\n\t\"os\"\n)\n\n// Fileinfo returns syscall fields from a File"
},
{
"path": "internal/limit_read_closer.go",
"chars": 558,
"preview": "package internal\n\nimport \"io\"\n\n// Copied from the io package to implement io.Closer.\nfunc LimitReadCloser(r io.ReadClose"
},
{
"path": "internal/lock_unix.go",
"chars": 1085,
"preview": "//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n\npackage internal\n\nimport (\n\t\""
},
{
"path": "internal/lock_windows.go",
"chars": 1063,
"preview": "//go:build windows\n\npackage internal\n\nimport (\n\t\"os\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nconst (\n\tsqlitePendingByte = 0x4000"
},
{
"path": "internal/resumable_reader.go",
"chars": 4044,
"preview": "package internal\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"github.com/superfly/ltx\"\n)\n\ntype LTXFileOpener interfa"
},
{
"path": "internal/resumable_reader_test.go",
"chars": 9348,
"preview": "package internal\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/superfly/ltx"
},
{
"path": "internal/testingutil/testingutil.go",
"chars": 16630,
"preview": "package testingutil\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"math/rand/v2\"\n\t\"net\"\n\t\"os\"\n\t"
},
{
"path": "leaser.go",
"chars": 995,
"preview": "package litestream\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n)\n\nvar ErrLeaseNotHeld = errors.New(\"lease not held\")\n\n"
},
{
"path": "litestream.go",
"chars": 5043,
"preview": "package litestream\n\nimport (\n\t\"database/sql\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv"
},
{
"path": "litestream_test.go",
"chars": 20766,
"preview": "package litestream_test\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com"
},
{
"path": "llms.txt",
"chars": 2119,
"preview": "# Litestream\n\nDisaster recovery tool for SQLite. Replicates WAL changes to S3, GCS, Azure, SFTP, or local filesystem.\n\n#"
},
{
"path": "log.go",
"chars": 265,
"preview": "package litestream\n\nconst (\n\tLogKeySystem = \"system\"\n\tLogKeySubsystem = \"subsystem\"\n\tLogKeyDB = \"db\"\n)\n\nconst "
},
{
"path": "mock/replica_client.go",
"chars": 1796,
"preview": "package mock\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"github.com/superfly/ltx\"\n\n\t\"github.com/benbjohnson/litestream\"\n)\n"
},
{
"path": "nats/replica_client.go",
"chars": 15421,
"preview": "package nats\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"sort\"\n\t\"strconv\"\n\t\"stri"
},
{
"path": "nats/replica_client_test.go",
"chars": 4706,
"preview": "package nats\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n)\n\nfunc TestReplicaClient_Type(t *testing.T) {\n\tcl"
},
{
"path": "oss/replica_client.go",
"chars": 19595,
"preview": "package oss\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"regexp\"\n\t\"string"
},
{
"path": "oss/replica_client_test.go",
"chars": 9116,
"preview": "package oss\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss\"\n)\n\nfunc TestReplicaClien"
},
{
"path": "packages/npm/litestream-vfs/README.md",
"chars": 716,
"preview": "# litestream-vfs\n\nLitestream VFS extension for SQLite — distributed via npm.\n\nThis package bundles the [Litestream](http"
},
{
"path": "packages/npm/litestream-vfs/index.js",
"chars": 1096,
"preview": "\"use strict\";\n\nconst path = require(\"path\");\nconst os = require(\"os\");\n\nconst PLATFORM_PACKAGES = {\n \"darwin-arm64\": \"l"
},
{
"path": "packages/npm/litestream-vfs/package.json",
"chars": 523,
"preview": "{\n \"name\": \"litestream-vfs\",\n \"version\": \"0.0.0\",\n \"description\": \"Litestream VFS extension for SQLite\",\n \"main\": \"i"
},
{
"path": "packages/npm/litestream-vfs-darwin-amd64/README.md",
"chars": 196,
"preview": "# litestream-vfs-darwin-amd64\n\nPlatform package for `litestream-vfs` — macOS x64 (Intel).\n\nThis package is installed aut"
},
{
"path": "packages/npm/litestream-vfs-darwin-amd64/package.json",
"chars": 287,
"preview": "{\n \"name\": \"litestream-vfs-darwin-amd64\",\n \"version\": \"0.0.0\",\n \"description\": \"Litestream VFS extension for macOS x6"
},
{
"path": "packages/npm/litestream-vfs-darwin-arm64/README.md",
"chars": 206,
"preview": "# litestream-vfs-darwin-arm64\n\nPlatform package for `litestream-vfs` — macOS ARM64 (Apple Silicon).\n\nThis package is ins"
},
{
"path": "packages/npm/litestream-vfs-darwin-arm64/package.json",
"chars": 291,
"preview": "{\n \"name\": \"litestream-vfs-darwin-arm64\",\n \"version\": \"0.0.0\",\n \"description\": \"Litestream VFS extension for macOS AR"
},
{
"path": "packages/npm/litestream-vfs-linux-amd64/README.md",
"chars": 187,
"preview": "# litestream-vfs-linux-amd64\n\nPlatform package for `litestream-vfs` — Linux x64.\n\nThis package is installed automaticall"
},
{
"path": "packages/npm/litestream-vfs-linux-amd64/package.json",
"chars": 306,
"preview": "{\n \"name\": \"litestream-vfs-linux-amd64\",\n \"version\": \"0.0.0\",\n \"description\": \"Litestream VFS extension for Linux x64"
},
{
"path": "packages/npm/litestream-vfs-linux-arm64/README.md",
"chars": 189,
"preview": "# litestream-vfs-linux-arm64\n\nPlatform package for `litestream-vfs` — Linux ARM64.\n\nThis package is installed automatica"
},
{
"path": "packages/npm/litestream-vfs-linux-arm64/package.json",
"chars": 310,
"preview": "{\n \"name\": \"litestream-vfs-linux-arm64\",\n \"version\": \"0.0.0\",\n \"description\": \"Litestream VFS extension for Linux ARM"
},
{
"path": "packages/python/MANIFEST.in",
"chars": 64,
"preview": "include README.md\nrecursive-include litestream_vfs *.so *.dylib\n"
},
{
"path": "packages/python/README.md",
"chars": 776,
"preview": "# litestream-vfs\n\nLitestream VFS extension for SQLite — distributed as a Python wheel.\n\nThis package bundles the [Litest"
},
{
"path": "packages/python/litestream_vfs/__init__.py",
"chars": 914,
"preview": "\"\"\"Litestream VFS extension for SQLite.\"\"\"\n\nimport os\nimport sys\n\n_EXT_MAP = {\n \"linux\": \"litestream-vfs.so\",\n \"da"
},
{
"path": "packages/python/litestream_vfs/noop.c",
"chars": 302,
"preview": "/* Dummy C extension to force wheel platform tagging. */\n#include <Python.h>\n\nstatic PyMethodDef methods[] = {{NULL, NUL"
},
{
"path": "packages/python/scripts/rename_wheel.py",
"chars": 1044,
"preview": "#!/usr/bin/env python3\n\"\"\"Rename a wheel file with the correct platform tag.\n\nUsage: python rename_wheel.py <wheel_dir> "
},
{
"path": "packages/python/setup.py",
"chars": 826,
"preview": "import os\nfrom setuptools import setup, Extension\n\nsetup(\n name=\"litestream-vfs\",\n version=os.environ.get(\"LITESTR"
},
{
"path": "packages/ruby/README.md",
"chars": 762,
"preview": "# litestream-vfs\n\nLitestream VFS extension for SQLite — distributed as a Ruby gem.\n\nThis gem bundles the [Litestream](ht"
},
{
"path": "packages/ruby/lib/litestream_vfs.rb",
"chars": 708,
"preview": "require \"rbconfig\"\n\nmodule LitestreamVfs\n EXT_MAP = {\n \"linux\" => \"litestream-vfs.so\",\n \"darwin\" => \"litestream-v"
},
{
"path": "packages/ruby/litestream-vfs.gemspec",
"chars": 625,
"preview": "Gem::Specification.new do |s|\n s.name = \"litestream-vfs\"\n s.version = ENV.fetch(\"LITESTREAM_VERSION\", \"0.0."
},
{
"path": "replica.go",
"chars": 50775,
"preview": "package litestream\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/f"
},
{
"path": "replica_client.go",
"chars": 5662,
"preview": "package litestream\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\n\t\"githu"
},
{
"path": "replica_client_test.go",
"chars": 31028,
"preview": "package litestream_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"math/rand\"\n\t\"os\"\n\t\"slices\"\n\t\""
},
{
"path": "replica_internal_test.go",
"chars": 11011,
"preview": "package litestream\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testi"
},
{
"path": "replica_test.go",
"chars": 71301,
"preview": "package litestream_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"string"
},
{
"path": "replica_url.go",
"chars": 11202,
"preview": "package litestream\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// ReplicaClientFactory is a func"
},
{
"path": "replica_url_test.go",
"chars": 35122,
"preview": "package litestream_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/benbjohnson/litestream\"\n\t\"github.com/benbjohnson/litestream/a"
},
{
"path": "restore_fuzz_test.go",
"chars": 3480,
"preview": "package litestream_test\n\nimport (\n\t\"math/rand\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/superfly/ltx\"\n\n"
},
{
"path": "s3/leaser.go",
"chars": 6926,
"preview": "package s3\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/aws/a"
},
{
"path": "s3/leaser_test.go",
"chars": 15158,
"preview": "package s3\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"sync\"\n\t\""
},
{
"path": "s3/replica_client.go",
"chars": 51532,
"preview": "package s3\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/md5\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slo"
}
]
// ... and 65 more files (download for full content)
About this extraction
This page contains the full source code of the benbjohnson/litestream GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 265 files (3.0 MB), approximately 810.6k tokens, and a symbol index with 1908 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.