pub struct GooseFs { /* private fields */ }services-goosefs only.Expand description
GooseFS services support via native gRPC.
§Capabilities
Depending on its configuration and the backing system, this service can expose:
- create_dir
- stat
- read
- write
- delete
- list
- copy
- rename
- rename (if_not_exists)
- presign
Inspect the effective capability set with opendal_core::Operator::info and
opendal_core::OperatorInfo::capability after building an operator.
§Notes
GooseFS service uses native gRPC protocol (not REST API like Alluxio), which means it connects directly to GooseFS Master (port 9200) and Worker (port 9203) without requiring a Proxy component.
Features:
- Read metadata: Streams expose full-file metadata from the SDK reader before consuming the body. Bounded reads do not issue a separate stat to obtain it.
- HA support: Comma-separated master addresses for automatic Primary Master discovery.
- Block-level I/O: Data reads/writes go through block-level gRPC bidirectional streaming.
- Consistent hash routing: Worker selection uses consistent hashing on block IDs.
- All WriteTypes: Supports MUST_CACHE, TRY_CACHE, CACHE_THROUGH, THROUGH, and ASYNC_THROUGH. Unknown
write_typevalues fail atbuild()withConfigInvalid. - Conditional Create:
write_with_if_not_existspublishes via Master no-replace rename (rename_with_if_not_exists); destination is never deleted on the Create path. - Rename parent directories:
renamecreates a missing destination parent only when Master reports it missing. Write-via-temp publish does not callCreateDirectoryfor a parent thatCreateFile(recursive)already created. - Optional client caches:
goosefs-sdk0.2.1 keeps metadata and page caches behind Cargo features so the default build stays a gRPC client. Enable them on this crate; see Optional client caches.
§Configuration
Use crate::GoosefsConfig for serializable configuration and this builder’s
methods for direct construction. The field and method documentation defines
accepted values, defaults, and environment interaction.
§Master address resolution
build() resolves the master addresses from three sources, highest priority
first:
- the
GOOSEFS_MASTER_ADDRenvironment variable, either a comma-separated list or the SDK’sgfs://h1:9200,h2:9200/rootURI form; goosefs.master.rpc.addressesorgoosefs.master.hostnameingoosefs-site.properties, discovered through$GOOSEFS_CONFIG_FILE,$GOOSEFS_CONF_DIR,$GOOSEFS_HOME/conf,~/.goosefs, and/etc/goosefs. Use$GOOSEFS_CONFIG_FILEto point at a file outside those directories;- the
master_addrconfig key, which also receives the URI authority ofgoosefs://host:port/path.
A site file that declares masters outranks master_addr because the file
carries a deployment’s whole HA master list, which a single URI authority
cannot express. Set GOOSEFS_MASTER_ADDR to override a deployed site file for
one process. build() fails with ConfigInvalid when no source supplies an
address; it never falls back to 127.0.0.1:9200.
goosefs-site.properties | GOOSEFS_MASTER_ADDR | master_addr / URI authority | Master addresses used |
|---|---|---|---|
| declares masters | set | any | GOOSEFS_MASTER_ADDR |
| declares masters | unset | any or absent | site file |
| absent, or no master keys | set | any | GOOSEFS_MASTER_ADDR |
| absent, or no master keys | unset | set | master_addr |
| absent, or no master keys | unset | absent | none — ConfigInvalid |
§Optional client caches
goosefs-sdk 0.2.1 compiles the client metadata cache and the disk-backed page
cache only when their Cargo features are enabled. The default
services-goosefs / opendal-service-goosefs build stays a gRPC client.
Enable a cache on this crate. Applications that use the opendal facade still
add opendal-service-goosefs as a direct dependency so Cargo unifies the
features into the same build:
| Feature | Compiles |
|---|---|
metadata-cache | Process-local status / listing LRU |
page-cache | Disk-backed page cache via tokio::fs |
page-cache-io-uring | Page cache plus the Linux io_uring backend |
cargo add opendal --features services-goosefs
cargo add opendal-service-goosefs --features metadata-cache,page-cache-io-uringpage-cache-io-uring already enables page-cache. On non-Linux targets the
SDK still builds the portable tokio::fs store; io_uring is Linux-only.
Compiling a cache does not replace runtime configuration. build() loads
goosefs-site.properties and GOOSEFS_* environment variables through
GoosefsConfig::from_properties_auto():
- Metadata cache: on by default once
metadata-cacheis compiled. SetGOOSEFS_METADATA_CACHE_ENABLED=falseto disable it. - Page cache: off by default. Set
GOOSEFS_USER_CLIENT_CACHE_ENABLED=trueand configure cache directories throughgoosefs.user.client.cache.dirs/GOOSEFS_USER_CLIENT_CACHE_DIRS.
See goosefs-sdk client configuration for the full cache knob list.
§Example
§Via Builder
use opendal::Operator;
use opendal::Result;
use opendal::services::GooseFs;
#[tokio::main]
async fn main() -> Result<()> {
// Single master
let builder = GooseFs::default()
.root("/data")
.master_addr("10.0.0.1:9200");
let op: Operator = Operator::new(builder)?;
Ok(())
}§Via URI
use opendal::Operator;
use opendal::Result;
#[tokio::main]
async fn main() -> Result<()> {
let op = Operator::from_uri("goosefs://10.0.0.1:9200/data")?;
Ok(())
}§HA Mode
use opendal::Operator;
use opendal::Result;
use opendal::services::GooseFs;
#[tokio::main]
async fn main() -> Result<()> {
let builder = GooseFs::default()
.root("/data")
.master_addr("10.0.0.1:9200,10.0.0.2:9200,10.0.0.3:9200")
.write_type("cache_through");
let op: Operator = Operator::new(builder)?;
Ok(())
}§With Authentication
use opendal::Operator;
use opendal::Result;
use opendal::services::GooseFs;
#[tokio::main]
async fn main() -> Result<()> {
// SIMPLE authentication (default) with custom username
let builder = GooseFs::default()
.root("/data")
.master_addr("10.0.0.1:9200")
.auth_type("simple")
.auth_username("myuser");
let op: Operator = Operator::new(builder)?;
// No authentication (NOSASL mode)
let builder = GooseFs::default()
.root("/data")
.master_addr("10.0.0.1:9200")
.auth_type("nosasl");
let op: Operator = Operator::new(builder)?;
Ok(())
}§Testing
This service is covered by all three OpenDAL test layers:
-
Unit tests (no cluster required) — exercise
Config/Builder/error-mapping boundaries. Run:cargo test -p opendal-service-goosefs -
Behavior tests (require a running GooseFS cluster) — the shared
core/tests/behaviorsuite (read/write/list/stat/rename/delete/create_dir). Start the fixture and point the harness at it:# Start a single-container GooseFS (master + worker; see start-default.sh) docker compose -f fixtures/goosefs/docker-compose-goosefs.yml up -d --wait OPENDAL_TEST=goosefs \ OPENDAL_GOOSEFS_MASTER_ADDR=127.0.0.1:9200 \ OPENDAL_GOOSEFS_ROOT=/ \ cargo test behavior --features tests,services-goosefs docker compose -f fixtures/goosefs/docker-compose-goosefs.yml downIf
OPENDAL_TESTis unset the behavior suite automatically skips, so missing a cluster is safe. -
GitHub CI —
.github/services/goosefs/goosefs/action.ymlis picked up automatically by.github/scripts/test_behavior/plan.py::provided_cases()and runs the fixture + behavior suite on every PR. The fixture image (ghcr.io/tencent/tencent-goosefs-rust-sdk/goosefs:v2.0.0) is public so no secrets are needed.
The fixture also exposes a distributed compose profile (separate master /
worker / job_master / job_worker containers) for multi-node diagnostics;
opt in via --profile distributed.
Implementations§
Source§impl GoosefsBuilder
impl GoosefsBuilder
Sourcepub fn root(self, root: &str) -> GoosefsBuilder
pub fn root(self, root: &str) -> GoosefsBuilder
Set root of this backend.
All operations will happen under this root.
Sourcepub fn master_addr(self, addr: &str) -> GoosefsBuilder
pub fn master_addr(self, addr: &str) -> GoosefsBuilder
Set master address(es).
Single master: "10.0.0.1:9200"
HA (comma-separated): "10.0.0.1:9200,10.0.0.2:9200,10.0.0.3:9200"
This is the lowest-priority source: build() uses it only when
neither GOOSEFS_MASTER_ADDR nor goosefs-site.properties declares a
master address, and fails with ConfigInvalid when no source supplies
one. See crate::GoosefsConfig::master_addr for the full resolution
order and its rationale.
Sourcepub fn block_size(self, size: u64) -> GoosefsBuilder
pub fn block_size(self, size: u64) -> GoosefsBuilder
Set block size for new files (bytes).
Sourcepub fn chunk_size(self, size: u64) -> GoosefsBuilder
pub fn chunk_size(self, size: u64) -> GoosefsBuilder
Set chunk size for streaming RPCs (bytes).
Sourcepub fn write_type(self, wt: &str) -> GoosefsBuilder
pub fn write_type(self, wt: &str) -> GoosefsBuilder
Set default write type.
Values: "must_cache", "try_cache", "cache_through", "through",
"async_through". Matching is case-insensitive. build() fails with
ErrorKind::ConfigInvalid when the value is not one of these.
Sourcepub fn auth_type(self, auth_type: &str) -> GoosefsBuilder
pub fn auth_type(self, auth_type: &str) -> GoosefsBuilder
Set authentication type.
Values: "nosasl", "simple" (default: "simple").
"nosasl"— skip SASL authentication entirely."simple"— PLAIN SASL with username (server does not verify password).
Sourcepub fn auth_username(self, username: &str) -> GoosefsBuilder
pub fn auth_username(self, username: &str) -> GoosefsBuilder
Set authentication username.
Used in SIMPLE mode as the login identity.
Default: current OS user ($USER / $USERNAME).
Trait Implementations§
Source§impl Builder for GoosefsBuilder
impl Builder for GoosefsBuilder
Source§impl Debug for GoosefsBuilder
impl Debug for GoosefsBuilder
Source§impl Default for GoosefsBuilder
impl Default for GoosefsBuilder
Source§fn default() -> GoosefsBuilder
fn default() -> GoosefsBuilder
Auto Trait Implementations§
impl Freeze for GoosefsBuilder
impl RefUnwindSafe for GoosefsBuilder
impl Send for GoosefsBuilder
impl Sync for GoosefsBuilder
impl Unpin for GoosefsBuilder
impl UnsafeUnpin for GoosefsBuilder
impl UnwindSafe for GoosefsBuilder
Blanket Implementations§
§impl<U> As for U
impl<U> As for U
§fn as_<T>(self) -> Twhere
T: CastFrom<U>,
fn as_<T>(self) -> Twhere
T: CastFrom<U>,
self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. Read moreSource§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::RequestSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
§impl<T> Scope for T
impl<T> Scope for T
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.