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.go files (via objectLayerImplementation wrapper). 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 implementation
    • xlStorageDiskIDCheck — wrapper that validates drive identity on every call
    • storageRESTClient — RPC proxy to a remote peer’s drive via the grid/REST layer
  • Design quality: Large but highly cohesive. The breadth mirrors ObjectLayer at 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):
    lock() *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) error
    Extended by the companion interface iamStorageWatcher:
    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. The iamStorageWatcher extension 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 remoteVersionID return type on Put lets 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 PutWithMeta variant 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 / SendFromStore split 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 a TargetStore (itself an interface with just Len()) 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. DRWMutex holds a slice of NetLocker — one per cluster peer — and uses quorum voting (N/2+1 confirmations) to grant locks. Refresh prevents lock staleness during long operations. The IsLocal() bool discriminator 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 (dynamicTimeout tracks lock wait times to auto-tune timeouts). Returns a LockContext that 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 by sync.RWMutex), distErasureLockInfo (distributed, backed by dsync.DRWMutex)
  • Design quality: The LockContext return 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] and StreamTypeHandler in 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 msgp code generator — dozens of *Request/*Response types across the cmd package.
  • Design quality: Novel use of Go generics (1.18+) to enforce serialization contracts at compile time. The comparable constraint 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 I allows 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 / PutRaw byte-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 (embeds msgp.Marshaler, msgp.Unmarshaler, msgp.Sizer) to compose serialization capabilities. iamStorageWatcher is not embedded in IAMStorageAPI but 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. The IAMStorageAPI (unexported) is an internal contract with no public API significance.

  • stdlib interfaces used:

    • io.Reader / io.ReadCloser — pervasive in StorageAPI and WarmBackend for streaming data
    • io.WriterStorageAPI.WalkDir writes metacache stream to an io.Writer
    • fmt.StringerStorageAPI.String(), NetLocker.String()
    • context.Context — every method in every interface; context is first-class throughout
    • http.Handler / http.ResponseWriter — used in the HTTP layer but not modeled as interfaces within MinIO itself

Key abstractions#

  1. 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).

  2. 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.

  3. dsync.NetLocker — The distributed coordination primitive. The quorum-based lock algorithm in DRWMutex runs over this interface — one instance per cluster peer. The IsLocal() discriminator cleanly optimizes the self-lock path without any extra branching at the usage site.

  4. 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.

  5. WarmBackend — The tiered storage abstraction. Five clean methods cover Put/Get/Remove for any cold storage backend. The remoteVersionID return 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 (ObjectLayererasureServerPools) 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).