# S3 ## Content # S3 (data driver) S3 data driver stores trace payloads in any S3-compatible object storage — AWS S3, Hetzner Object Storage, Cloudflare R2, minio and friends. The client is injected, so the gem does not depend on `aws-sdk-s3`. ## Configuration ### `client: (Aws::S3::Client)` Any object compatible with the `Aws::S3::Client` interface: ```ruby require 'aws-sdk-s3' s3 = Aws::S3::Client.new( endpoint: 'https://fsn1.your-objectstorage.com', access_key_id: '...', secret_access_key: '...', region: 'us-east-1', force_path_style: true ) Trifle::Traces.configure do |config| config.data_driver = Trifle::Traces::Driver::Data::S3.new( client: s3, buckets: %w[traces-a traces-b] ) end ``` ### `buckets: (Array)` One or more buckets. With multiple buckets, each trace picks a random bucket at liftoff (stored as `bucket_id` on the record) and keeps using it for all its parts and artifacts. This spreads writes across per-bucket rate limits at high volume. ### `prefix: 'traces' (String)` Key prefix inside the bucket. ### `gzip: false (Boolean)` When enabled, payload parts are gzip-compressed (`data_1.json.gz`). Trace payloads are JSON text and compress well — recommended for production volume. ## Storage layout ``` ////data_.json[.gz] ////artifacts/ ``` For example `3/traces/commodity/poll/6a4b.../data_1.json.gz`. Retention days come first so payload expiry is a bucket lifecycle rule per retention class — never per tenant or namespace. ## Setup Creates one lifecycle expiration rule per retention class on every bucket. Run once: ```ruby Trifle::Traces::Driver::Data::S3.setup!( client: s3, buckets: %w[traces-a traces-b], retentions: [3, 7, 180] ) ``` After that, objects under `3/traces/` expire after 3 days, `7/traces/` after 7, and so on — storage cleans itself up.