Goosefs Rust gRPC Client
A native Rust client library that communicates directly with Goosefs Master/Worker via gRPC (tonic/protobuf).
What's New in v0.1.7
- Client-side local page cache — New opt-in, disk-backed page cache mirroring the GooseFS Java client's
goosefs.user.client.cache.*semantics.LocalCacheManagerprovides striped page locks, LRU/LFU evictors, multi-directoryHashAllocator, bounded async write-back, TTL lazy expiry with a background sweeper, restart restore, and overwrite invalidation viaon_file_open. Integrated intoGoosefsFileInStream::read/read_atthroughread_through_cache;ReadType::NoCachestill serves hits but skips back-fill. Best-effort by design — misses/errors always fall back to the worker without affecting read correctness. AddsClient.Cache*metrics (incl.HitRate,SpaceUsedCount, external read time). Seedocs/CLIENT_PAGE_CACHE_DESIGN.mdanddocs/CLIENT_CONFIGURATION.md. - Short-Circuit local mmap read path — New
short_circuitmodule that bypasses the gRPC data plane when the client and worker are co-located.LocalBlockReaderperforms zero-copy reads via read-onlymmapwithmadviseprefetch and optional Transparent Huge Pages (THP);ShortCircuitFactoryprovides per-task hot-block caches, negative caching, aCapabilityProviderhook, and a context-shared factory. A dedicatedSIGBUSdiagnostic handler surfaces mmap faults with actionable diagnostics and manages process-level signal installation. Local worker is auto-detected by interface bind. Every recoverable error transparently falls back to the standard gRPC path. Wired into both sequential (read()) and positioned-read (read_at()) paths throughfile_in_stream/context/config. Ships with ansc_pr_abbenchmark comparing local mmap vs gRPC positioned-read, gated E2E integration tests, and an INV-S3/INV-D1/INV-D2/INV-S1/INV-S2/INV-S5 consistency regression suite. Server-side companion: newOpenLocalBlockRPC +OpenLocalBlockGuardfor block-lock lifecycle. Seedocs/SHORT_CIRCUIT_DESIGN.md. - Read-path performance optimization (wait-free hot paths) —
WorkerClientPool.clientsandWorkerRouter.workers/hash_ring/local_worker_idare nowArcSwapinstead ofRwLock<HashMap>, mirroring the existingArcSwap<AuthedState>model onMasterClient. The acquire andselect_workerhot paths become a single atomic load + map lookup + cheap clone (no asyncRwLockround-trip); writes useArcSwap::rcucopy-on-write, and same-key reconnects are still single-flighted by the per-key mutex — generation / single-flight / invalidate semantics preserved. Local A/B (--transport=block, 64 threads / 16 MiB): 64 KiB742.8 → 897.1 MiB/s (+20.8%), 256 KiB1381.8 → 1434.3 (+3.8%), 1 MiB1564.4 → 1742.4 (+11.4%); p999 −64% (64 KiB) / −52% (256 KiB). Seebenchmarks/pr_runtime_ab.rsanddocs/RUST_PYTHON_SDK_OPTIMIZATION.mdV.6. - Batch metadata / lifecycle APIs —
BaseFileSystemgains batch entry points that fan out over the concurrent path with a sharedArc<BaseFileSystem>(single Tokio spawn per batch, first-error-wins). Exposed to the Python binding asAsyncGoosefs.batch_open_file/batch_create_file/batch_create_dir/batch_rename/batch_delete/batch_list_status(plus their syncGoosefs.batch_*counterparts). One PyO3 boundary crossing per batch instead of N. Includes aWorkerRouterinit deferral (WorkerManageroptional, first-write initialization) so batch-metadata-only workloads avoid paying the Worker-plane setup cost. - Reliability / robustness —
PollingMasterInquireClientHA primary discovery is now cancel-safe via a new RAIILeaderGuard(no more infinite recursion when the singleflight leader is cancelled by an outertimeout/select!).WriteBlockHandle::Dropnow aborts the background gRPC task on early-error paths instead of leaking a detached future.GoosefsFileWriter::Dropperforms best-effort cleanup (cancels in-flight cache/UFS streams, callsmaster.remove_blocksor falls back todelete(unchecked=true)).LogSampleruses monotonicInstant(safe under NTP / admin clock jumps).MetricsMasterClient::with_retryreconnects at the top of the next attempt.WorkerClient::connectnow setsrequest_timeout.config::parse_byte_sizeoverflow is a hard error (previously silently wrapped).WorkerRouterconsistent-hash ring is pre-built onupdate_workers(O(log N)binary_searchper request), andpick_any_workerusesrand::Rng::random_rangefor proper load spreading. - No breaking API changes — Drop-in upgrade from
0.1.6; downstreamOpenDAL/Lanceintegrations require no code changes.
Why Goosefs?
Goosefs is a high-performance distributed caching file system built on top of COS (Cloud Object Storage). It accelerates data access for big data and AI/ML workloads by providing a unified namespace and intelligent caching layer between compute engines and cloud storage.
Why Goosefs Rust Client?
This is a standalone Rust gRPC client crate (Layer 3) in the Lance → OpenDAL → Goosefs architecture. It talks directly to Goosefs Master and Worker services over gRPC, enabling:
- Native performance — Zero-copy block streaming with bidirectional gRPC, no JNI/FFI overhead
- Async-first — Built entirely on
tokio+tonicfor high-concurrency I/O - Lance integration — Designed as the foundation for the OpenDAL Goosefs backend powering Lance vector storage acceleration
┌────────────────────────────────────────────────────────────────┐
│ Layer 1 — Lance Provider (lance-io / ObjectStore) │
├────────────────────────────────────────────────────────────────┤
│ Layer 2 — OpenDAL Goosefs Service (opendal::services) │
├────────────────────────────────────────────────────────────────┤
│ Layer 3 — Goosefs Rust gRPC Client ← this crate │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ★ FileSystem Abstraction (recommended entry point) │ │
│ │ FileSystem trait + BaseFileSystem │ │
│ │ FileSystemContext — shared connection pool │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ ★ High-Level I/O │ │
│ │ GoosefsFileInStream — seekable dual-path read stream │ │
│ │ GoosefsFileWriter — end-to-end file write pipeline │ │
│ │ GoosefsFileReader — end-to-end file read pipeline │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ MasterClient — File metadata CRUD (Master:9200) │ │
│ │ WorkerMgrClient — Worker discovery (Master:9200) │ │
│ │ VersionClient — Service handshake (Master:9200) │ │
│ │ WorkerClient — Block streaming (Worker:9203) │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ ChannelAuthenticator — SASL auth (NOSASL / SIMPLE) │ │
│ │ SaslClientHandler — PLAIN SASL handshake │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ BlockMapper — file range → block read plans │ │
│ │ WorkerRouter — consistent hash + local-first routing │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ GrpcBlockReader — streaming + positioned read │ │
│ │ GrpcBlockWriter — bidirectional streaming write │ │
│ ├──────────────────────────────────────────────────────────┤ │
│ │ Metrics Registry — global counters / gauges │ │
│ │ HeartbeatTask — periodic delta report → Master │ │
│ │ PushgatewayTask — periodic push to Prometheus GW │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
Quick Start
Step 1: Start a Goosefs Cluster
Requirements
Goosefs runs on all UNIX-like environments (Linux, macOS). Make sure you have:
- Java 11 (required — set
JAVA_HOMEaccordingly) - A running Goosefs Master (default RPC port
9200) and at least one Worker (default data port9203)
# Example: start Goosefs locally (adjust paths to your installation)
export JAVA_HOME=/path/to/jdk-11
cd /path/to/goosefs
./bin/goosefs-start.sh local SudoMount
Verify the cluster is healthy:
./bin/goosefs fs ls /
Requirements (Rust side)
- Rust 1.88+ — Install via rustup
- protoc — Protocol Buffers compiler (needed by
tonic-buildat compile time)
# macOS
brew install protobuf
# Ubuntu / Debian
sudo apt install -y protobuf-compiler
Step 2: Build the Client
git clone <repo-url> goosefs-client-rust
cd goosefs-client-rust
cargo build
Step 3: Use as a Dependency
Add to your project's Cargo.toml:
[]
= { = "../goosefs-client-rust" }
= { = "1", = ["full"] }
Example: File Metadata Operations
use MasterClient;
use GoosefsConfig;
async
Example: Multi-Master Connection
use GoosefsConfig;
use MasterClient;
async
Example: FileSystem API (Recommended)
use GoosefsConfig;
use FileSystemContext;
use ;
use SeekFrom;
async
Example: High-Level File Write (Recommended)
use GoosefsFileWriter;
use GoosefsConfig;
use WritePType;
async
Example: High-Level File Read (Recommended)
use GoosefsFileReader;
use GoosefsConfig;
async
Example: Client Local Page Cache
The SDK ships an optional client-side local page cache (mirrors the GooseFS
Java client's goosefs.user.client.cache.*). When enabled, ranges read from a
worker/UFS are cached on the local disk in fixed-size pages; subsequent reads
of the same range are served straight from local disk — no worker round-trip.
This is a big win for repeated-epoch AI training, Parquet/ORC random small I/O,
and hot small-file reads.
- Disabled by default — existing behavior is unchanged unless you opt in.
- Best-effort — any cache error or miss transparently falls back to reading from the worker/UFS; the cache never affects correctness.
- Transparent — random (
read_at) reads onGoosefsFileInStreamroute through the cache once enabled, with no API change. Sequential (read) reads bypass the cache by default to avoid read amplification; enableclient_cache_sequential_read_enabledto cache them too. Note: the cache is consulted by the seekable streaming reader (GoosefsFileInStream/ Pythonfs.open_file(...)). The one-shotGoosefsFileReader::read_file/read_rangeandpositioned_readhelpers use the worker-direct path and do not consult the local cache — read via the streaming reader to benefit from caching. - Overwrite-safe — on (re)open the cache compares the file's
(length, last_modification_time); if the file changed, its stale pages are invalidated automatically. (Best-effort: relies on the UFSmtimegranularity — use a TTL where only second-levelmtimeis available.) - Survives restarts — pages and their identity persisted on disk are restored on startup, so overwrite detection still applies to restored pages.
use Arc;
use GoosefsConfig;
use FileSystemContext;
use GoosefsFileInStream;
use OpenFileOptions;
async
Configuration is also accepted via goosefs-site.properties keys,
GOOSEFS_USER_CLIENT_CACHE_* environment variables, and (from Python) the
Config(properties={...}) dict:
| Property key | Field | Default |
|---|---|---|
goosefs.user.client.cache.enabled |
client_cache_enabled |
false |
goosefs.user.client.cache.page.size |
client_cache_page_size |
1MB |
goosefs.user.client.cache.size |
client_cache_size |
20 GiB |
goosefs.user.client.cache.dirs |
client_cache_dirs |
/tmp/goosefs_cache |
goosefs.user.client.cache.eviction.policy |
client_cache_evictor |
LFU |
goosefs.user.client.cache.async.write.enabled |
client_cache_async_write_enabled |
true |
goosefs.user.client.cache.async.write.threads |
client_cache_async_write_threads |
16 |
goosefs.user.client.cache.quota.enabled |
client_cache_quota_enabled |
false |
goosefs.user.client.cache.ttl.seconds |
client_cache_ttl_secs |
0 (no expiry) |
goosefs.user.client.cache.sequential.read.enabled |
client_cache_sequential_read_enabled |
false |
goosefs.user.client.cache.uring.enabled |
client_cache_uring_enabled |
true on Linux / false elsewhere |
goosefs.user.client.cache.uring.queue.depth |
client_cache_uring_queue_depth |
32768 |
goosefs.user.client.cache.uring.thread.count |
client_cache_uring_thread_count |
2 |
Cache effectiveness is observable via Client.Cache* metrics (e.g.
CacheBytesReadCache vs CacheBytesReadExternal, CachePages,
CacheBytesEvicted), reported through the same heartbeat/Pushgateway pipeline
as other client metrics.
Tip: Run
cargo run --example page_cache_demofor an end-to-end demo that writes a file, then proves cold-miss → back-fill → warm-hit using theClient.Cache*metrics. (SetGOOSEFS_AUTH_TYPE=nosaslif your dev cluster runs without SASL.)More cache coverage:
- A/B throughput benchmark:
cargo run --release --example page_cache_ab- Integration tests (live cluster):
GOOSEFS_AUTH_TYPE=nosasl cargo test --test page_cache_e2e -- --ignored- Python e2e:
GOOSEFS_MASTER_ADDR=127.0.0.1:9200 GOOSEFS_AUTH_TYPE=nosasl uv run --group test pytest tests/test_page_cache.py(inbindings/python)
Example: Client Metrics & Heartbeat
The SDK ships a built-in client-metrics pipeline. When metrics_enabled = true
(the default), each FileSystemContext spawns a background HeartbeatTask
that periodically reports incremental counter deltas to the GooseFS Master
via the MetricsHeartbeat RPC. The io layer auto-increments well-known
counters (Client.BytesReadLocal, Client.BytesWrittenLocal), and your
application can register additional counters/gauges via the global registry.
use Arc;
use Duration;
use GoosefsConfig;
use FileSystemContext;
use ;
use metrics;
async
To disable the entire metrics pipeline (no background task, no RPC overhead):
let config = new
.with_metrics_enabled;
Every knob below can also be set without touching Rust code:
goosefs-site.properties— e.g.goosefs.user.metrics.collection.enabled=false(mirrors the Java client key). Seedocs/METRICS.md§5.4 for the full list.- Environment variables — e.g.
GOOSEFS_USER_METRICS_COLLECTION_ENABLED=false(highest priority, overlays both defaults and the properties file). This is the recommended switch for operators who want to toggle the heartbeat on a running deployment without redeploying application code. Python users get the same behaviour:Config(...),Config.from_uri(...)andConfig.from_properties_file(...)all overlayGOOSEFS_*env vars on top of the caller-supplied configuration.
Configuration knobs
| Field | Default | Description |
|---|---|---|
metrics_enabled |
true |
Master switch — when false the heartbeat task is not spawned. |
metrics_heartbeat_interval |
10 s |
Period between heartbeat reports. Must be >= 1 s. |
metrics_heartbeat_timeout |
3 s |
Per-RPC timeout. Must be >= 1 s and < metrics_heartbeat_interval. |
metrics_max_batch_size |
512 |
Max number of metric entries packed into a single heartbeat. |
app_id |
None |
Optional client tag attached to every heartbeat (useful for grouping in Master logs). |
Built-in counter names (re-exported from goosefs_sdk::metrics::name):
Client.BytesReadLocal— bytes read via local short-circuit (auto-incremented by theiolayer).Client.BytesWrittenLocal— bytes written via local short-circuit (auto-incremented).Client.BytesWrittenUfs— bytes written directly to UFS (bypassing the cache).
Short-circuit (local mmap) read counters (see docs/SHORT_CIRCUIT_DESIGN.md):
Client.ShortCircuitOpenSuccess— successfulOpenLocalBlock+ mmap sessions.Client.ShortCircuitOpenLocalFail—OpenLocalBlockRPC failures (block not local / IO error).Client.ShortCircuitFileOpenFail—File::openfailures on the local block path (e.g. EACCES).Client.ShortCircuitMmapFail—mmapfailures (ENOMEM / EINVAL / size mismatch).Client.ShortCircuitReadBytes— total bytes served from the local mmap path.Client.ShortCircuitReadCalls— number of short-circuit read calls.Client.ShortCircuitCacheHits— hot-block reader LRU hits (mmap reused, no new open).Client.ShortCircuitCacheEvictions— reader-cache evictions.Client.ShortCircuitNegCacheHits— negative-cache hits (recently-failed block skipped → gRPC).Client.ShortCircuitActiveReaders— currently-live short-circuit readers (gauge).Client.ShortCircuitPrefetchCalls—prefetch/prefetch_manycalls.Client.ShortCircuitPrefetchBytes— cumulative bytes requested for prefetch.Client.ShortCircuitPrefetchMadvise— actualmadvise(WILLNEED)syscalls issued (after coalescing).
Tip: Run
cargo run --example metrics_heartbeatfor an end-to-end demo that exercises bothmetrics_enabled = trueandmetrics_enabled = false. SetRUST_LOG=infoto see the SDK's heartbeat / flush logs.
Example: Authentication
use AuthType;
use MasterClient;
use GoosefsConfig;
async
Authentication Guide
Goosefs supports two authentication modes. The Rust client must use the mode that matches the server-side configuration, otherwise RPCs will be rejected with Unauthenticated.
Authentication Modes
| Mode | Server Config | Description |
|---|---|---|
| NOSASL | goosefs.security.authentication.type=NOSASL |
No SASL handshake. The client generates a local channel-id for API consistency, but the server does not verify any credentials. Suitable for development/testing environments. |
| SIMPLE | goosefs.security.authentication.type=SIMPLE |
PLAIN SASL handshake. The client sends a username via a bidirectional gRPC stream (SaslAuthenticationService/Authenticate), and the server returns a channel-id upon success. All subsequent RPCs carry this channel-id in gRPC metadata. This is the default and recommended mode. |
Server-Side Configuration
Set the authentication type in conf/goosefs-site.properties on the Goosefs Master/Worker:
# Option 1: SIMPLE authentication (recommended, default)
# Option 2: No authentication (development only)
# goosefs.security.authentication.type=NOSASL
Important: After changing the authentication type, you must restart the Goosefs cluster for the change to take effect.
Client-Side Configuration
use AuthType;
use GoosefsConfig;
use Duration;
// ── SIMPLE mode (default) ──
// GoosefsConfig::new() defaults to SIMPLE + current OS username.
// No extra configuration needed in most cases.
let config = new;
// ── SIMPLE mode with explicit username ──
let config = new
.with_auth_type
.with_auth_username;
// ── SIMPLE mode with custom auth timeout ──
let config = new
.with_auth_type
.with_auth_username
.with_auth_timeout;
// ── NOSASL mode ──
// Use only when the server is configured with NOSASL.
let config = new
.with_auth_type;
Default Behavior
| Config Field | Default Value | Description |
|---|---|---|
auth_type |
AuthType::Simple |
Authentication mode |
auth_username |
Current OS username ($USER / $USERNAME) |
Username sent during SASL handshake |
auth_timeout |
10 seconds | Timeout for the SASL authentication handshake |
Common Errors
| Error | Cause | Solution |
|---|---|---|
Channel: xxx is not authenticated |
Client uses NOSASL but server requires SIMPLE | Change client to .with_auth_type(AuthType::Simple) |
SASL authentication failed |
Server uses NOSASL but client sends SASL handshake | Change client to .with_auth_type(AuthType::NoSasl) |
Connection timeout during auth |
Network issue or server not responding | Check server status; increase auth_timeout |
Tip: Run
cargo run --example auth_demofor a comprehensive authentication demo that tests both modes.
Example: Block-Level Streaming Read
use ;
use ;
use GrpcBlockReader;
use GoosefsConfig;
async
Modules
| Module | Description |
|---|---|
fs::FileSystem |
FileSystem trait — high-level async interface (get_status, list_status, exists, open_file, create_file, mkdir, delete, rename). Object-safe via async_trait, Send+Sync+'static. |
fs::BaseFileSystem |
Production FileSystem implementation — supports shared-context mode via FileSystemContext and legacy per-call mode. Implements WriteType xattr inheritance. exists() follows Java semantics (INCOMPLETE non-folder → false). |
context::FileSystemContext |
Shared connection pool — three-layer architecture eliminating repeated TCP+SASL handshakes. Holds Arc<MasterClient> + Arc<WorkerClientPool> + Arc<WorkerRouter>. Background worker-list refresh (30s) and config hot-reload (60s). |
io::GoosefsFileInStream |
Seekable dual-path file input stream — sequential reads via block_in_stream (streaming, prefetch) and random reads via positioned_read (position_short=true). Auto-switches based on 8 KiB threshold. Supports seek(SeekFrom) and read_at(). |
io::GoosefsFileWriter |
High-level file writer — one-shot write_file() or builder pattern create() → write() → close(). Supports all 4 WriteTypes. Cancel/close state machine with UUID-based idempotent FsOpPId. |
io::GoosefsFileReader |
High-level file reader — one-shot read_file() / read_range() or streaming open() → read_next_block(). Orchestrates GetStatus → BlockMapper → WorkerRouter → GrpcBlockReader |
io::GoosefsAsyncReader |
AsyncRead/AsyncSeek adapter — wraps GoosefsFileInStream and implements tokio::io::AsyncRead + tokio::io::AsyncSeek for seamless integration with the tokio I/O ecosystem. |
fs::URIStatus |
Immutable file/directory metadata snapshot converted from proto FileInfo. Typed accessors for all metadata fields. |
fs::options |
Rust-native options structs — OpenFileOptions, CreateFileOptions, DeleteOptions, InStreamOptions, ReadType |
auth::ChannelAuthenticator |
SASL authentication for gRPC channels — supports NOSASL (no handshake) and SIMPLE (PLAIN SASL) |
auth::AuthType |
Authentication type enum — NoSasl, Simple (default). Corresponds to Java's goosefs.security.authentication.type |
client::MasterClient |
File system metadata CRUD — get_status, list_status, create_file, complete_file (with idempotent FsOpPId), remove_blocks, delete, rename, create_directory, schedule_async_persistence |
client::MasterInquireClient |
Master discovery with singleflight deduplication — only one task polls when multiple callers need the primary address simultaneously |
client::WorkerManagerClient |
Worker discovery — get_worker_info_list |
client::WorkerClient |
Bidirectional streaming block read/write — read_block, read_block_positioned (position_short=true), write_block(options: WriteBlockOptions) |
client::WorkerClientPool |
Connection pool for reusing authenticated worker gRPC channels |
block::BlockMapper |
Converts file-level byte ranges into block-level read/write plans |
block::WorkerRouter |
Consistent-hash routing with TTL-based worker list refresh (30s), local-worker preference (mirrors Java LocalFirstPolicy), and failure tracking |
io::GrpcBlockReader |
Low-level streaming block reader with flow-control ACK + positioned_read() for random access |
io::GrpcBlockWriter |
Low-level streaming block writer with chunk splitting and flush |
config::GoosefsConfig |
Connection configuration — 30+ settings including properties file parsing, YAML auto-config, ConfigRefresher hot-reload, TransparentAccelerationSwitch, timeouts, block/chunk size, write/read types, auth, multi-master, worker routing |
WritePType |
Write type enum — MustCache, TryCache, CacheThrough, Through, AsyncThrough, None |
metrics::registry |
Global metrics registry — process-wide thread-safe Counter / Gauge factories (metrics::counter(name), metrics::gauge(name)) plus the metrics::name::* constants for SDK-managed counters. |
metrics::HeartbeatTask |
Background heartbeat task — owned by FileSystemContext, periodically computes counter deltas via ClientMetricsReporter and ships them to the Master through MetricsHeartbeat. Honors metrics_heartbeat_interval / metrics_heartbeat_timeout / metrics_max_batch_size and performs a final flush on close(). |
metrics::pushgateway |
Prometheus Pushgateway reporter — PushgatewayTask periodically collects all metrics from the global registry and pushes them to a Pushgateway endpoint via HTTP POST in Prometheus text exposition format. Configurable job/instance labels, push interval, and graceful shutdown. |
error::Error |
Unified error type with domain-specific variants (FileIncomplete, DirectoryNotEmpty, OpenDirectory, InvalidPath, AuthenticationFailed) mapped from Java server exceptions |
gRPC Services
This client wraps 5 Goosefs gRPC services defined in 12 proto files:
| Service | Port | Proto | Key RPCs |
|---|---|---|---|
FileSystemMasterClientService |
Master:9200 | file_system_master.proto |
GetStatus, ListStatus, CreateFile, CompleteFile, Delete, Rename, CreateDirectory … (37 RPCs) |
BlockWorker |
Worker:9203 | block_worker.proto |
ReadBlock (bidi-stream), WriteBlock (bidi-stream), AsyncCache, RemoveBlock … (12 RPCs) |
WorkerManagerMasterClientService |
Master:9200 | worker_manager_master.proto |
GetWorkerInfoList, GetCapacityBytes, GetUsedBytes … (9 RPCs) |
ServiceVersionClientService |
Master:9200 | version.proto |
GetServiceVersion |
SaslAuthenticationService |
Master:9200 / Worker:9203 | sasl_server.proto |
Authenticate (bidi-stream) — SASL handshake for channel authentication |
Project Structure
goosefs-client-rust/
├── Cargo.toml # crate manifest
├── build.rs # tonic-build proto compilation
├── proto/ # Goosefs protobuf definitions (11 files)
│ ├── grpc/ # Master/Worker service protos
│ └── proto/ # Shared data types (security, acl, status)
├── src/
│ ├── lib.rs # crate root & proto module tree
│ ├── config.rs # GoosefsConfig (properties/YAML/hot-reload, 30+ keys)
│ ├── context.rs # ★ FileSystemContext (shared connection pool)
│ ├── error.rs # Error enum (domain-specific variants)
│ ├── auth/
│ │ ├── mod.rs # Auth module root
│ │ ├── authenticator.rs # ChannelAuthenticator + AuthType
│ │ └── sasl_client.rs # PLAIN SASL handshake handler
│ ├── client/
│ │ ├── master.rs # MasterClient (idempotent FsOpPId)
│ │ ├── master_inquire.rs # MasterInquireClient (singleflight)
│ │ ├── worker.rs # WorkerClient + WorkerClientPool
│ │ └── worker_manager.rs # WorkerManagerClient
│ ├── block/
│ │ ├── mapper.rs # BlockMapper (file → block plans)
│ │ └── router.rs # WorkerRouter (consistent hash + TTL + local-first)
│ ├── fs/ # ★ FileSystem abstraction layer
│ │ ├── mod.rs # Module root + re-exports
│ │ ├── filesystem.rs # FileSystem trait (async_trait)
│ │ ├── base_filesystem.rs # BaseFileSystem (production impl)
│ │ ├── options.rs # OpenFileOptions, CreateFileOptions, etc.
│ │ ├── uri_status.rs # URIStatus (immutable metadata snapshot)
│ │ └── write_type.rs # WriteType xattr helpers
│ ├── io/
│ │ ├── file_in_stream.rs # ★ GoosefsFileInStream (seekable dual-path)
│ │ ├── async_reader.rs # ★ GoosefsAsyncReader (AsyncRead + AsyncSeek)
│ │ ├── file_reader.rs # GoosefsFileReader (high-level)
│ │ ├── file_writer.rs # GoosefsFileWriter (cancel/close state machine)
│ │ ├── reader.rs # GrpcBlockReader (streaming + positioned)
│ │ └── writer.rs # GrpcBlockWriter (low-level)
│ ├── metrics/ # ★ Client metrics & heartbeat pipeline
│ │ ├── mod.rs # Module root + public re-exports
│ │ ├── registry.rs # Global Counter/Gauge registry + name constants
│ │ ├── reporter.rs # ClientMetricsReporter (snapshot + delta calc)
│ │ ├── heartbeat.rs # HeartbeatTask (periodic MetricsHeartbeat RPC)
│ │ └── pushgateway.rs # ★ PushgatewayTask (Prometheus Pushgateway push)
│ └── generated/ # prost/tonic generated code (checked-in; shipped with the crate)
├── examples/
│ ├── highlevel_file_rw.rs # ★ High-level file read/write (recommended)
│ ├── streaming_file_read.rs # ★ Streaming read — constant O(block) memory
│ ├── seekable_file_read.rs # ★ Seekable read via GoosefsFileInStream (seek / read_at)
│ ├── context_file_rw.rs # ★ FileSystemContext shared connection pool
│ ├── page_cache_demo.rs # ★ Client local page cache (cold miss → back-fill → warm hit)
│ ├── write_types.rs # ★ WriteType comparison
│ ├── ha_multi_master.rs # ★ Multi-master mode
│ ├── auth_demo.rs # ★ Authentication demo (NOSASL / SIMPLE)
│ ├── metrics_heartbeat.rs # ★ Client metrics & heartbeat demo
│ ├── lowlevel_block_read.rs # Low-level block streaming read
│ ├── lowlevel_create_file.rs # Low-level file creation (metadata only)
│ ├── metadata_crud.rs # File/directory metadata CRUD
│ └── async_persistence.rs # Async persistence scheduling
├── tests/
│ └── connection_reuse.rs # Connection reuse integration test
├── bindings/
│ └── python/ # ★ Python SDK (PyO3 + maturin)
│ ├── python/goosefs/ # Python package source
│ ├── src/ # Rust PyO3 bridge
│ └── pyproject.toml # Build configuration
└── target/ # build artifacts (git-ignored)
Development
Build
cargo build
Test
cargo test
Build with Release Optimizations
cargo build --release
Clean Build Artifacts
cargo clean
Build Python Bindings
The goosefs Python package lives under bindings/python/
and is built with maturin. For local development:
cd bindings/python
uv sync --all-extras --group dev --group test # one-time environment setup
uv run maturin develop --uv # compile + install as editable
See bindings/python/DEVELOPMENT.md for the full
build/test/lint loop.
Release
| Artifact | Guide |
|---|---|
Rust crate (goosefs-sdk) → crates.io / Cargo registry |
docs/RELEASE.md |
Python package (goosefs) → PyPI (manylinux wheels) |
docs/PYTHON_RELEASE.md |
Re-generate Proto Code
This crate ships pre-generated protobuf code under src/generated/, so downstream users do NOT need protoc installed to build goosefs-sdk — a regular cargo build just works out of the box.
The regeneration flow is opt-in and only required when you modify any .proto file under proto/. To regenerate:
# Requires `protoc` (>= 3.15) on PATH.
GOOSEFS_SDK_REGEN_PROTO=1 cargo build
The updated .rs files will be written back to src/generated/ — commit them along with your .proto changes so that downstream users continue to get a zero-protoc build.
Why the opt-in design? Running
tonic-build::compile_protoson everycargo buildwould force all downstream users to installprotoc, and would also breakcargo publishverification (the package tarball is read-only). Shipping pre-generated code follows the same approach asetcd-clientandtonic-health.
Key Dependencies
| Crate | Version | Purpose |
|---|---|---|
tonic |
0.14 | gRPC framework (HTTP/2 + protobuf) |
prost |
0.14 | Protobuf code generation & runtime |
tokio |
1.x | Async runtime |
tokio-stream |
0.1 | Stream utilities for bidirectional gRPC |
bytes |
1.x | Zero-copy byte buffers |
thiserror |
2.x | Ergonomic error derives |
dashmap |
6.x | Concurrent hash map (failure tracking) |
tracing |
0.1 | Structured logging |
serde |
1.x | Config serialization |
uuid |
1.x | Channel-id generation for SASL authentication |
hostname |
0.3 | Local worker detection for routing preference |
reqwest |
0.12 | HTTP client for Pushgateway push |
rand |
0.9 | Random jitter for retry backoff |
async-trait |
0.1 | Async trait support for FileSystem trait |
Goosefs Compatibility
| Goosefs Version | Java | Status |
|---|---|---|
| Latest (JDK 11) | Java 11 | ✅ Supported |
Note: Goosefs requires Java 11. Make sure
JAVA_HOMEpoints to a JDK 11 installation when running the Goosefs cluster.
License
Licensed under the Apache License, Version 2.0.