MinIO — Interfaces#
Interface catalog#
ObjectLayer#
- Package:
cmd - File:
cmd/object-api-interface.go:246 - Methods (40+):
NewNSLock(bucket string, objects ...string) RWLocker Shutdown(context.Context) error NSScanner(ctx, updates chan<- DataUsageInfo, wantCycle uint32, scanMode) error BackendInfo() madmin.BackendInfo Legacy() bool StorageInfo(ctx, metrics bool) StorageInfo LocalStorageInfo(ctx, metrics bool) StorageInfo MakeBucket(ctx, bucket string, opts MakeBucketOptions) error GetBucketInfo(ctx, bucket string, opts BucketOptions) (BucketInfo, error) ListBuckets(ctx, opts BucketOptions) ([]BucketInfo, error) DeleteBucket(ctx, bucket string, opts DeleteBucketOptions) error ListObjects(ctx, bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, error) ListObjectsV2(...) (ListObjectsV2Info, error) ListObjectVersions(...) (ListObjectVersionsInfo, error) Walk(ctx, bucket, prefix string, results chan<- itemOrErr[ObjectInfo], opts WalkOptions) error GetObjectNInfo(ctx, bucket, object string, rs *HTTPRangeSpec, h http.Header, opts ObjectOptions) (*GetObjectReader, error) GetObjectInfo(ctx, bucket, object string, opts ObjectOptions) (ObjectInfo, error) PutObject(ctx, bucket, object string, data *PutObjReader, opts ObjectOptions) (ObjectInfo, error) CopyObject(ctx, srcBucket, srcObject, destBucket, destObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (ObjectInfo, error) DeleteObject(ctx, bucket, object string, opts ObjectOptions) (ObjectInfo, error) DeleteObjects(ctx, bucket string, objects []ObjectToDelete, opts ObjectOptions) ([]DeletedObject, []error) TransitionObject(ctx, bucket, object string, opts ObjectOptions) error RestoreTransitionedObject(ctx, bucket, object string, opts ObjectOptions) error ListMultipartUploads(...) (ListMultipartsInfo, error) NewMultipartUpload(...) (*NewMultipartUploadResult, error) CopyObjectPart(...) (PartInfo, error) PutObjectPart(...) (PartInfo, error) GetMultipartInfo(...) (MultipartInfo, error) ListObjectParts(...) (ListPartsInfo, error) AbortMultipartUpload(...) error CompleteMultipartUpload(...) (ObjectInfo, error) GetDisks(poolIdx, setIdx int) ([]StorageAPI, error) SetDriveCounts() []int HealFormat(ctx, dryRun bool) (madmin.HealResultItem, error) HealBucket(ctx, bucket string, opts madmin.HealOpts) (madmin.HealResultItem, error) HealObject(ctx, bucket, object, versionID string, opts madmin.HealOpts) (madmin.HealResultItem, error) HealObjects(ctx, bucket, prefix string, opts madmin.HealOpts, fn HealObjectFn) error CheckAbandonedParts(ctx, bucket, object string, opts madmin.HealOpts) error Health(ctx, opts HealthOptions) HealthResult PutObjectMetadata(ctx, string, string, ObjectOptions) (ObjectInfo, error) DecomTieredObject(ctx, string, string, FileInfo, ObjectOptions) error PutObjectTags(ctx, string, string, string, ObjectOptions) (ObjectInfo, error) GetObjectTags(ctx, string, string, ObjectOptions) (*tags.Tags, error) DeleteObjectTags(ctx, string, string, ObjectOptions) (ObjectInfo, error) - Purpose: The single seam between the HTTP API layer and all storage implementations. Defines the complete S3-shaped contract — buckets, objects, multipart, versioning, tagging, healing, tiering, and health — in one interface. Every HTTP handler calls into this interface exclusively; no storage code is called directly.
- Implementations:
erasureServerPools(production distributed backend);cacheObjects(a caching wrapper); test stubs in_test.gofiles (viaobjectLayerImplementationwrapper). Historical gateway implementations have been removed from the current codebase. - Design quality: Deliberately not ISP-compliant — this is a God interface by design. It captures the full S3 contract in one place, making the seam explicit and making the storage layer swappable as a whole. The trade-off is width: implementations must satisfy all 40+ methods even if they delegate many. The comment history shows the team made this choice intentionally (vs. splitting into sub-interfaces) to avoid partial implementations leaking implementation details.
StorageAPI#
- Package:
cmd - File:
cmd/storage-interface.go:29 - Methods (~35):
String() string IsOnline() bool LastConn() time.Time IsLocal() bool Hostname() string Endpoint() Endpoint Close() error GetDiskID() (string, error) SetDiskID(id string) Healing() *healingTracker DiskInfo(ctx, opts DiskInfoOptions) (DiskInfo, error) NSScanner(ctx, cache dataUsageCache, updates chan<- dataUsageEntry, scanMode, shouldSleep func() bool) (dataUsageCache, error) MakeVol(ctx, volume string) error MakeVolBulk(ctx, volumes ...string) error ListVols(ctx) ([]VolInfo, error) StatVol(ctx, volume string) (VolInfo, error) DeleteVol(ctx, volume string, forceDelete bool) error WalkDir(ctx, opts WalkDirOptions, wr io.Writer) error DeleteVersion(ctx, volume, path string, fi FileInfo, forceDelMarker bool, opts DeleteOptions) error DeleteVersions(ctx, volume string, versions []FileInfoVersions, opts DeleteOptions) []error DeleteBulk(ctx, volume string, paths ...string) error WriteMetadata(ctx, origvolume, volume, path string, fi FileInfo) error UpdateMetadata(ctx, volume, path string, fi FileInfo, opts UpdateMetadataOpts) error ReadVersion(ctx, origvolume, volume, path, versionID string, opts ReadOptions) (FileInfo, error) ReadXL(ctx, volume, path string, readData bool) (RawFileInfo, error) RenameData(ctx, srcVolume, srcPath string, fi FileInfo, dstVolume, dstPath string, opts RenameOptions) (RenameDataResp, error) ListDir(ctx, origvolume, volume, dirPath string, count int) ([]string, error) ReadFile(ctx, volume, path string, offset int64, buf []byte, verifier *BitrotVerifier) (int64, error) AppendFile(ctx, volume, path string, buf []byte) error CreateFile(ctx, origvolume, volume, path string, size int64, reader io.Reader) error ReadFileStream(ctx, volume, path string, offset, length int64) (io.ReadCloser, error) RenameFile(ctx, srcVolume, srcPath, dstVolume, dstPath string) error RenamePart(ctx, srcVolume, srcPath, dstVolume, dstPath string, meta []byte, skipParent string) error CheckParts(ctx, volume, path string, fi FileInfo) (*CheckPartsResp, error) Delete(ctx, volume, path string, opts DeleteOptions) error VerifyFile(ctx, volume, path string, fi FileInfo) (*CheckPartsResp, error) StatInfoFile(ctx, volume, path string, glob bool) ([]StatInfo, error) ReadParts(ctx, bucket string, partMetaPaths ...string) ([]*ObjectPartInfo, error) ReadMultiple(ctx, req ReadMultipleReq, resp chan<- ReadMultipleResp) error CleanAbandonedData(ctx, volume, path string) error WriteAll(ctx, volume, path string, b []byte) error ReadAll(ctx, volume, path string) ([]byte, error) GetDiskLoc() (poolIdx, setIdx, diskIdx int) - Purpose: Defines the per-drive filesystem contract. Abstracts over local POSIX drives (
xlStorage) and remote drives accessed over HTTP (storageRESTClient). Enables the erasure layer to treat all drives uniformly regardless of locality. Includes both file-level operations (ReadFile, WriteAll) and MinIO-specific versioned metadata operations (ReadVersion, WriteMetadata, RenameData). - Implementations:
xlStorage— local POSIX filesystem implementationxlStorageDiskIDCheck— wrapper that validates drive identity on every callstorageRESTClient— RPC proxy to a remote peer’s drive via the grid/REST layer
- Design quality: Large but highly cohesive. The breadth mirrors
ObjectLayerat one level down. All operations share a common shape (context-first, volume+path addressing, FileInfo metadata). The drive identity validation wrapper (xlStorageDiskIDCheck) is a textbook decorator pattern.
IAMStorageAPI#
- Package:
cmd - File:
cmd/iam-store.go:592 - Methods (~20, all unexported):
Extended by the companion interfacelock() *iamCache unlock() rlock() *iamCache runlock() getUsersSysType() UsersSysType loadPolicyDoc(ctx, policy string, m map[string]PolicyDoc) error loadPolicyDocWithRetry(ctx, policy string, m map[string]PolicyDoc, retries int) error loadPolicyDocs(ctx, m map[string]PolicyDoc) error loadUser(ctx, user string, userType IAMUserType, m map[string]UserIdentity) error loadSecretKey(ctx, user string, userType IAMUserType) (string, error) loadUsers(ctx, userType IAMUserType, m map[string]UserIdentity) error loadGroup(ctx, group string, m map[string]GroupInfo) error loadGroups(ctx, m map[string]GroupInfo) error loadMappedPolicy(ctx, name string, userType IAMUserType, isGroup bool, m *xsync.MapOf[string, MappedPolicy]) error loadMappedPolicyWithRetry(ctx, name string, ..., retries int) error loadMappedPolicies(ctx, userType IAMUserType, isGroup bool, m *xsync.MapOf[string, MappedPolicy]) error saveIAMConfig(ctx, item any, path string, opts ...options) error loadIAMConfig(ctx, item any, path string) error deleteIAMConfig(ctx, path string) error savePolicyDoc(ctx, policyName string, p PolicyDoc) error saveMappedPolicy(ctx, name string, userType IAMUserType, isGroup bool, mp MappedPolicy, opts ...options) error saveUserIdentity(ctx, name string, userType IAMUserType, u UserIdentity, opts ...options) error saveGroupInfo(ctx, group string, gi GroupInfo) error deletePolicyDoc(ctx, policyName string) error deleteMappedPolicy(ctx, name string, userType IAMUserType, isGroup bool) error deleteUserIdentity(ctx, name string, userType IAMUserType) error deleteGroupInfo(ctx, name string) erroriamStorageWatcher:watch(ctx context.Context, keyPath string) <-chan iamWatchEvent - Purpose: Pluggable persistence layer for IAM state (users, groups, policies, mappings). Allows IAM data to be stored in the object store itself (
IAMObjectStore) or in etcd (IAMEtcdStore). The lock/unlock methods are part of the interface because the IAM cache is owned by the store — callers must acquire the store’s lock to safely read or write cached state. TheiamStorageWatcherextension is satisfied only by the etcd store, enabling real-time config propagation across nodes. - Implementations:
IAMObjectStore(stores IAM config in.minio.sys/bucket),IAMEtcdStore(stores in etcd for multi-cluster setups) - Design quality: All methods are unexported, making this a package-internal abstraction — not part of MinIO’s public API. Unusual in that the locking protocol is exposed in the interface itself (the caller is expected to hold the lock returned by
lock()/rlock()when modifying the cache). This is a deliberate coupling of the storage and cache layers to avoid a separate mutex hierarchy.
WarmBackend#
- Package:
cmd - File:
cmd/warm-backend.go:39 - Methods:
Put(ctx context.Context, object string, r io.Reader, length int64) (remoteVersionID, error) PutWithMeta(ctx context.Context, object string, r io.Reader, length int64, meta map[string]string) (remoteVersionID, error) Get(ctx context.Context, object string, rv remoteVersionID, opts WarmBackendGetOpts) (io.ReadCloser, error) Remove(ctx context.Context, object string, rv remoteVersionID) error InUse(ctx context.Context) (bool, error) - Purpose: Abstracts remote tier storage (S3, GCS, Azure, Minio, filesystem) for ILM-driven object tiering (transitioning cold objects to cheaper storage). The
remoteVersionIDreturn type onPutlets each backend track their own versioning scheme opaquely. - Implementations:
warmBackendS3,warmBackendGCS,warmBackendAzure,warmBackendMinIO,warmBackendDisk— one per supported remote tier type. - Design quality: Well-segregated (ISP-compliant). Five methods is the right size for a storage backend abstraction at this level. The
PutWithMetavariant handles the metadata-aware case without bloating the core Put path.
event.Target#
- Package:
internal/event - File:
internal/event/targetlist.go:41 - Methods:
ID() TargetID IsActive() (bool, error) Save(Event) error SendFromStore(store.Key) error Close() error Store() TargetStore - Purpose: Defines the contract for event notification destinations (Kafka, NATS, Redis, Elasticsearch, AMQP, webhooks, etc.). The
Save/SendFromStoresplit implements a store-and-forward pattern: events are durably written to a local store first (Save), then delivered to the remote target (SendFromStore). This provides at-least-once delivery without blocking the S3 write path. - Implementations: One per notification target type —
KafkaTarget,NATSTarget,RedisTarget,ElasticsearchTarget,AMQPTarget,WebhookTarget,NSQTarget,MQTTTarget,PostgresTarget,MySQLTarget. - Design quality: Clean, well-focused. The
Store()accessor returning aTargetStore(itself an interface with justLen()) is a minimal observability hook. The two-phase delivery (Save + SendFromStore) is elegant — it decouples the write critical path from network reliability.
dsync.NetLocker#
- Package:
internal/dsync - File:
internal/dsync/locker.go:23 - Methods:
RLock(ctx context.Context, args LockArgs) (bool, error) Lock(ctx context.Context, args LockArgs) (bool, error) RUnlock(ctx context.Context, args LockArgs) (bool, error) Unlock(ctx context.Context, args LockArgs) (bool, error) Refresh(ctx context.Context, args LockArgs) (bool, error) ForceUnlock(ctx context.Context, args LockArgs) (bool, error) String() string Close() error IsOnline() bool IsLocal() bool - Purpose: Peer-to-peer distributed locking protocol. Each node acts as both a locker client and a lock server.
DRWMutexholds a slice ofNetLocker— one per cluster peer — and uses quorum voting (N/2+1 confirmations) to grant locks.Refreshprevents lock staleness during long operations. TheIsLocal() booldiscriminator allows the locker to skip the network for self-locks. - Implementations:
lockRESTClient(HTTP-based locker RPC to remote peers),localLocker(in-process locking for the local node) - Design quality: Well-designed. The (bool, error) return on lock operations is idiomatic for distributed systems — the bool signals the lock result, and error signals a transport failure, which have different semantics in quorum voting.
RWLocker#
- Package:
cmd - File:
cmd/namespace-lock.go:40 - Methods:
GetLock(ctx context.Context, timeout *dynamicTimeout) (lkCtx LockContext, timedOutErr error) Unlock(lkCtx LockContext) GetRLock(ctx context.Context, timeout *dynamicTimeout) (lkCtx LockContext, timedOutErr error) RUnlock(lkCtx LockContext) - Purpose: High-level namespace locking interface returned by
ObjectLayer.NewNSLock(). Provides context-cancellation-aware R/W locking with adaptive timeouts (dynamicTimeouttracks lock wait times to auto-tune timeouts). Returns aLockContextthat wraps a cancellable context — when the lock is released, the context is cancelled, ensuring any ongoing operation within the lock scope is notified. - Implementations:
nsLock(in-process, backed bysync.RWMutex),distErasureLockInfo(distributed, backed bydsync.DRWMutex) - Design quality: The
LockContextreturn type is a MinIO-specific innovation — it ties lock lifetime to context lifetime, propagating cancellation automatically. Clean 4-method interface.
grid.RoundTripper (generic constraint)#
- Package:
internal/grid - File:
internal/grid/handlers.go:400 - Methods:
msgp.Unmarshaler (embedded) msgp.Marshaler (embedded) msgp.Sizer (embedded) comparable (type constraint) - Purpose: Type constraint for the generics-based
SingleHandler[Req, Resp RoundTripper]andStreamTypeHandlerin the grid RPC system. Any message type used in grid RPC must be msgpack-serializable and comparable (for nil checks). This constraint drives the use of code-generated msgpack types throughout the codebase. - Implementations: Generated types via
msgpcode generator — dozens of*Request/*Responsetypes across thecmdpackage. - Design quality: Novel use of Go generics (1.18+) to enforce serialization contracts at compile time. The
comparableconstraint embedded in an interface is idiomatic Go 1.21+ style. Avoids reflection at the RPC call site entirely.
store.Store[I any]#
- Package:
internal/store - File:
internal/store/store.go:47 - Methods:
Put(item I) (Key, error) PutMultiple(item []I) (Key, error) Get(key Key) (I, error) GetMultiple(key Key) ([]I, error) GetRaw(key Key) ([]byte, error) PutRaw(b []byte) (Key, error) Len() int List() []Key Del(key Key) error Open() error Delete() error - Purpose: Generic durable queue interface used by the event notification and logger subsystems for store-and-forward delivery. Events and log entries are written here on the fast path; background workers drain and deliver them. The generic parameter
Iallows the same queue implementation to be reused for different event types without reflection. - Implementations:
QueueStore[I](filesystem-backed), in-memory variants for tests. - Design quality: Good use of generics introduced in Go 1.18. The interface is appropriately wide for a durable queue (open, close, list, put, get, delete).
GetRaw/PutRawbyte-level methods alongside the typed API suggest practical performance optimizations for bulk operations.
Interface patterns#
Size distribution: Bimodal. The two God interfaces (
ObjectLayer~40 methods,StorageAPI~35 methods) anchor one end. Most other interfaces are 4–8 methods (WarmBackend,NetLocker,RWLocker,event.Target). A few are single-method helpers (iamStorageWatcher,TargetStore). The median is ~6 methods.Embedding: Used in
grid.RoundTripper(embedsmsgp.Marshaler,msgp.Unmarshaler,msgp.Sizer) to compose serialization capabilities.iamStorageWatcheris not embedded inIAMStorageAPIbut checked via type assertion at runtime — a deliberate choice to make the watch capability opt-in.Implicit satisfaction: Mixed. The large interfaces (
ObjectLayer,StorageAPI) are provider-defined — the interface lives near the abstraction, and implementations fill it. Smaller interfaces (WarmBackend,event.Target) are consumer-defined — the interface lives with the system that uses it, and backends implement it. TheIAMStorageAPI(unexported) is an internal contract with no public API significance.stdlib interfaces used:
io.Reader/io.ReadCloser— pervasive inStorageAPIandWarmBackendfor streaming dataio.Writer—StorageAPI.WalkDirwrites metacache stream to anio.Writerfmt.Stringer—StorageAPI.String(),NetLocker.String()context.Context— every method in every interface; context is first-class throughouthttp.Handler/http.ResponseWriter— used in the HTTP layer but not modeled as interfaces within MinIO itself
Key abstractions#
ObjectLayer— The architectural keystone. Every line of S3 API code calls into this interface. It is simultaneously the most important abstraction and the least ISP-compliant. Its deliberate breadth means swapping storage implementations is an all-or-nothing proposition — which is exactly what MinIO intends (there is only one real implementation).StorageAPI— The drive-level contract. Enables the erasure layer to treat local POSIX drives and remote HTTP-proxied drives identically. The decorator pattern (xlStorageDiskIDCheck) built on this interface is the main hook for per-call drive validation.dsync.NetLocker— The distributed coordination primitive. The quorum-based lock algorithm inDRWMutexruns over this interface — one instance per cluster peer. TheIsLocal()discriminator cleanly optimizes the self-lock path without any extra branching at the usage site.event.Target— The extensibility seam for notifications. MinIO supports 10+ notification backends; this interface is how each one is integrated. The store-and-forward pattern (Save + SendFromStore) baked into the interface design gives all backends reliable delivery for free.WarmBackend— The tiered storage abstraction. Five clean methods cover Put/Get/Remove for any cold storage backend. TheremoteVersionIDreturn type is a subtle but important detail: it lets each backend track objects in their own native version scheme without MinIO needing to understand it.
Interface-driven extensibility#
MinIO uses interfaces at three distinct extensibility seams:
1. Storage backends (WarmBackend): Third-party cold storage (S3-compatible services, GCS, Azure, filesystem) is plugged in via WarmBackend. This is the most plug-in-friendly interface: adding a new tier backend means implementing 5 methods.
2. Event notification targets (event.Target): New notification destinations (message queues, databases, webhooks) are registered by implementing the Target interface. The store-and-forward infrastructure (queue management, retry, stats) is provided by the framework; the target only implements the actual delivery logic.
3. IAM persistence (IAMStorageAPI): The IAM system can store its state either in the object store itself or in etcd. The IAMStorageAPI interface, while package-private, is the extension point for alternative IAM backends. The optional iamStorageWatcher companion interface enables push-based config propagation for backends (etcd) that support it.
Not interface-driven: The core storage path (ObjectLayer → erasureServerPools) is effectively a closed system. The ObjectLayer interface exists for testability and conceptual clarity, but there is only one production implementation. MinIO made the deliberate choice to not support pluggable storage backends at the ObjectLayer level (the gateway mode that previously allowed this has been removed).