Amazon S3 and S3-compatible storage
OptunaSharp.Artifacts.S3 stores artifact bytes in an existing bucket through the AWS SDK for .NET. Its public type is Boto3ArtifactStore, retaining the upstream Optuna class name for cross-reference; it does not use Python or boto3. The package targets .NET 8 and .NET 10 and references OptunaSharp and AWSSDK.S3. Package definition.
dotnet add package OptunaSharp.Artifacts.S3
Construct the store
Boto3ArtifactStore(
string bucketName,
IAmazonS3? client = null,
bool avoidBufCopy = false);
Omitting client creates an AmazonS3Client with ambient AWS credentials and region settings. Supply your own client when configuring an endpoint, authentication, timeouts, or SDK retry behavior. The adapter does not create the bucket or configure its access policy. Its calls are object get, put, and delete, so the configured identity needs the operations your application uses. Constructor and requests.
using System;
using System.IO;
using Amazon.S3;
using OptunaSharp;
using OptunaSharp.Artifacts;
using var client = new AmazonS3Client();
var store = new Boto3ArtifactStore("optimization-artifacts", client, avoidBufCopy: true);
var study = Optuna.CreateStudy();
// model.bin must already exist. Attaching directly to the study is supported.
string id = ArtifactFunctions.UploadArtifact(store, "model.bin", study);
Directory.CreateDirectory("restored");
ArtifactFunctions.DownloadArtifact(store, Path.Combine("restored", $"{id}.bin"), id);
Keep the injected client alive until all transfers finish, then dispose it in the application that owns it. Boto3ArtifactStore has no Dispose method and does not dispose an injected client. The convenience constructor creates a client internally without exposing a disposal hook, so injection is useful when lifetime must be explicit. Store implementation.
Transfer behavior
The bucket is fixed at construction and the object key is exactly artifactId. UploadArtifact generates that ID and records the original filename separately in study/trial metadata; see the shared artifact workflow.
By default, writes copy the remaining input into a MemoryStream, rewind the buffer, and upload it with PutObjectAsync. avoidBufCopy: true passes the caller's stream directly, avoiding this additional full-artifact allocation. Both modes set AutoCloseStream = false, so the SDK must not close the caller's stream. The wrapper disposes only its own temporary buffer. No multipart orchestration is implemented in the adapter: it calls the SDK's PutObjectAsync once per write. It blocks on the SDK's asynchronous get/put/delete methods to implement the synchronous artifact interface. Transfer implementation.
Reads return the SDK response stream; use DownloadArtifact to copy and dispose it, or dispose it yourself when using OpenReader. NoSuchKey and any HTTP 404 become ArtifactNotFound, with the original SDK exception retained as the inner exception. Other errors, including access denied, propagate as SDK exceptions. Deleting an absent key follows S3's idempotent behavior and succeeds. No content type, content encoding, ACL, encryption, version selection, or object prefix is added to requests by this adapter. Unit tests.
Other S3 endpoints
A caller-supplied IAmazonS3 enables S3-compatible services. For example, the repository's local MinIO tests create an AmazonS3Client with ServiceURL and ForcePathStyle = true and inject it into this constructor. Configure the endpoint and credentials for the particular service; the adapter does not detect a service from the bucket name. Emulator client configuration.
MinIO contract tests cover empty and nonempty round trips, replacement, nested IDs, absent reads/deletes, and both buffering modes. They establish the adapter's exercised S3 behavior; they are not live AWS deployment tests. Use the R2 integration for its account endpoint convenience constructor. For retry boundaries, metadata consistency, and local download conflicts, see Cloud artifact stores.