End-to-end scenarios — swift — branch stable/2026.2
Branch
stable/2026.2@a1bdaeba21bd— repository https://opendev.org/openstack/swift — analysed on 2026-10-29
Six flows that cover the cooperation of proxies, storage servers and daemons. Statuses and errors refer to 02-errors_stable-2026.2.md; frames and formats to 03-interactions_stable-2026.2.md; daemons to 05-async-processing_stable-2026.2.md.
1. Create a container (and its account) with Keystone
sequenceDiagram
participant C as Client (swiftclient / Skyline)
participant P as Proxy (authtoken + keystoneauth)
participant K as Keystone
participant MC as memcached
participant A as Account servers x3
participant CS as Container servers x3
C->>P: PUT /v1/AUTH_<project>/photos X-Auth-Token, X-Storage-Policy: gold
P->>K: GET /v3/auth/tokens (validate, cached in memcache)
K-->>P: 200 project, roles
P->>P: keystoneauth: project matches account and role in operator_roles → swift_owner
P->>MC: get account/AUTH_<project>
alt account info not cached
P->>A: HEAD /<dev>/<part>/AUTH_<project>
A-->>P: 404 (new project)
P->>A: PUT /<dev>/<part>/AUTH_<project> (account_autocreate)
A-->>P: 201 x3
end
P->>CS: PUT /<dev>/<part>/AUTH_<project>/photos X-Account-Host/Partition/Device, X-Backend-Storage-Policy-Index: 0
CS->>CS: create <hash>.db (broker initialize)
CS->>A: PUT /<dev>/<part>/AUTH_<project>/photos X-Put-Timestamp X-Object-Count: 0
A-->>CS: 201
CS-->>P: 201 x3
P-->>C: 201 Created
alt policy name unknown or deprecated
P-->>C: 400
else a replica already has the container under another policy
CS-->>P: 409 → P-->>C: 409 (quorum) or 202
endSources: swift/common/middleware/keystoneauth.py:401, swift/proxy/controllers/container.py:635, swift/proxy/controllers/account.py:154, swift/container/server.py:496, swift/container/server.py:209.
2. Erasure-coded PUT with two-phase commit
sequenceDiagram
participant C as Client
participant P as Proxy (ECObjectController)
participant O as Object servers x6 (4+2)
participant CS as Container servers
C->>P: PUT /v1/AUTH_a/c/big (1 GiB, chunked)
P->>O: PUT /<dev>/<part>/AUTH_a/c/big X-Backend-Obj-Multipart-Mime-Boundary, -Metadata-Footer: yes, -Multiphase-Commit: yes, Expect: 100-continue
O-->>P: 100 Continue (X-Obj-Metadata-Footer: yes, X-Obj-Multiphase-Commit: yes)
loop each 1 MiB segment
P->>P: PyECLib encode → 6 fragments
P->>O: fragment i to node i (inside "X-Document: object body")
end
P->>O: "X-Document: object metadata" footer JSON (EC etag, sizes, frag index, Content-MD5)
O->>O: write <ts>#<i>.data (non-durable), xattrs
O-->>P: 100 Continue (second)
alt fewer than ec_ndata+1 nodes acknowledged
P-->>C: 503
else quorum reached
P->>O: "X-Document: put commit" put_commit_confirmation
O->>O: rename to <ts>#<i>#d.data (durable)
O->>CS: PUT .../c/big X-Size: <client size> X-Etag: <client md5> (container update override)
O-->>P: 201
P-->>C: 201 ETag: <client md5>
endSources: swift/proxy/controllers/obj.py:2858, swift/proxy/controllers/obj.py:1976, swift/obj/server.py:893, swift/obj/server.py:1029, swift/obj/diskfile.py:3391, swift/obj/server.py:606. Non-durable fragments left by a failed commit are either made durable by the reconstructor when it finds enough of them, or reclaimed after reclaim_age (swift/obj/diskfile.py:1120).
3. GET while a primary is down, then repair
sequenceDiagram participant C as Client participant P as Proxy participant O1 as Primary 1 (down) participant O2 as Primary 2 participant H as Handoff node participant R as object-replicator on O2 C->>P: GET /v1/AUTH_a/c/o P->>O1: GET (connect timeout conn_timeout 0.5 s) O1--xP: timeout → node error-limited after 10 errors/60 s P->>O2: GET O2-->>P: 200 (stream) P-->>C: 200 Note over P,H: Meanwhile a PUT during the outage went to O2, O3 and handoff H (get_more_nodes) R->>O1: REPLICATE /<dev>/<part> (after O1 is back) O1-->>R: pickled suffix hashes R->>O1: rsync changed suffixes (xattrs included) then REPLICATE /<dev>/<part>/<sfx> Note over H: replicator on H pushes its handoff partition to all primaries, then deletes it
Sources: swift/proxy/controllers/base.py:1383 (GetOrHeadHandler), swift/common/ring/ring.py:561, swift/obj/replicator.py:649, swift/obj/replicator.py:434, swift/obj/replicator.py:619.
4. Upload and download a segmented image (Glance swift store, DLO)
sequenceDiagram
participant G as glance-api (swift store)
participant P as Proxy
G->>P: HEAD /v1/AUTH_svc/glance (container exists? else PUT if create_container_on_put)
loop chunk n (200 MiB each, swift_store_large_object_chunk_size)
G->>P: PUT /v1/AUTH_svc/glance/<image_id>-0000n (ETag checked by swiftclient)
P-->>G: 201 ETag
end
G->>P: PUT /v1/AUTH_svc/glance/<image_id> X-Object-Manifest: glance/<image_id>- (0 bytes)
P-->>G: 201
G->>P: GET /v1/AUTH_svc/glance/<image_id>
P->>P: dlo: list glance?prefix=<image_id>- then stream segments in order
P-->>G: 200, ETag "<md5 of segment etags>"
G->>P: DELETE (image delete): HEAD manifest, list segments, DELETE each, DELETE manifestSources: glance_store:glance_store/_drivers/swift/store.py:963, glance_store:glance_store/_drivers/swift/store.py:1010, glance_store:glance_store/_drivers/swift/store.py:1090, glance_store:glance_store/_drivers/swift/store.py:1142, swift/common/middleware/dlo.py:245. A 409 on the manifest PUT means "image already exists" for Glance; a 413 means "storage full" (glance_store:glance_store/_drivers/swift/store.py:1130).
5. S3 request authenticated by Keystone (s3api + s3token)
sequenceDiagram
participant S as S3 client (boto3)
participant P as Proxy (s3api, s3token, keystoneauth)
participant K as Keystone
S->>P: PUT /bucket/key Authorization: AWS4-HMAC-SHA256 Credential=<access>/…, x-amz-content-sha256
P->>P: s3api: parse SigV4, build the string to sign, map to PUT /v1/AUTH_?/bucket/key
P->>K: POST /v3/s3tokens {"credentials": {access, token (string to sign), signature}}
alt signature valid
K-->>P: 200 token (project, roles)
P->>P: s3token sets X-Project-Id, X-Roles…; account AUTH_<project>
P->>P: keystoneauth authorises; object PUT as in 03 § 8
P-->>S: 200 ETag (S3 XML for errors)
else invalid
K-->>P: 401
P-->>S: 403 <Error><Code>SignatureDoesNotMatch</Code>…
endSources: swift/common/middleware/s3api/s3api.py:212, swift/common/middleware/s3api/s3token.py:266, swift/common/middleware/s3api/s3token.py:117.
6. Object expiry
sequenceDiagram
participant C as Client
participant P as Proxy
participant O as Object servers
participant Q as Container servers of .expiring_objects
participant E as object-expirer
C->>P: PUT /v1/AUTH_a/c/o X-Delete-After: 3600
P->>O: PUT … X-Delete-At: <t>, X-Delete-At-Host/Device/Partition/Container
O->>Q: PUT /<dev>/<part>/.expiring_objects/<task container>/<t>-AUTH_a/c/o
O-->>P: 201
Note over O: after <t>, GET returns 404 at once (DiskFileExpired)
E->>Q: list task containers ≤ now, list task objects (InternalClient)
E->>O: DELETE /v1/AUTH_a/c/o X-If-Delete-At: <t> X-Timestamp: <t>
alt object rewritten with another expiry
O-->>E: 412 → task removed, object kept
else
O-->>E: 204 (tombstone, listing tombstone)
end
E->>Q: DELETE task objectSources: swift/proxy/controllers/obj.py:494, swift/obj/server.py:453, swift/obj/expirer.py:97, swift/obj/expirer.py:553, swift/obj/server.py:1354.
Source: reverse-spec-swift/04-scenarios_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.