OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

swift — Scenarios

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
  end

Sources: 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>
  end

Sources: 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 manifest

Sources: 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>…
  end

Sources: 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 object

Sources: 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.