Repository navigation
Conversation
Contributor
Author
|
Here's some useful diagrams: https://claude.ai/artifact/MFc9D3SkkE2Y8uTsFUrT2f?sk=LwuBqkn1c776XsbMZy5FuA |
swartzn
force-pushed
the
swartzn/feat/cli-managed-remote-storage-targets
branch
4 times, most recently
from
November 9, 2026 13:01
468731f to
51af297
Compare
swartzn
changed the base branch from
main
to
swartzn/fix/remote-node-connection-handler
November 9, 2026 13:02
Remote storage targets could only be defined in the beegfs-remote TOML file, and changing one needed a restart of Remote and every sync node. Targets that shared a store had to repeat its endpoint and credentials. Add `beegfs remote target create|modify|delete|list`. Remote stores the definitions in a new BadgerDB (job.rst-db) and applies each change live, to itself and to every sync node. On first start it imports the TOML targets. After that the database is the only source. Add backends, managed with `beegfs remote backend create|modify|delete|list|verify`. A backend is the named connection to one remote store. A target of the same type names it with --backend and inherits every setting it leaves unset. Remote stores only what each one sets, so a change to a backend reaches every target that names it. Each RST type describes its configuration through rst.TargetType, which the CLI, Remote and rst.New use instead of switching on the type. The type also marks which settings a backend must set, and which it must not set. For S3 and xtreemstore, a backend must set endpoint-url, region, access-key and secret-key, and must not set bucket. Rules: - Every target passes one check, however it arrives: create, modify, a backend change, or the TOML import. Its type must be one a client supports, so Mock is refused. It must have every setting its type requires, and a provider must build from it. - A target's name is optional, as it was in the TOML file. Jobs, stubs and every command name a target by its ID. A name that is given must be at most 256 bytes, valid UTF-8 and free of control characters. A backend's name follows the same rule and is required, because targets refer to the backend by it. - An S3 or xtreemstore target must set endpoint-url. Before, an empty endpoint-url let the S3 library pick one on each node, from that node's environment, its shared config file or the region. So Remote and the sync nodes could disagree. A TOML file that relied on this stops Remote at the upgrade. The shipped TOML says how to fix it. - A backend must set region for the same reason. With no region, the S3 library takes one from each host's environment, and fails without one. - Nothing checks the form of a value, such as the endpoint URL. Only contacting the store proves a value works. - A target may set any setting itself, including one its backend must set, and its own value wins. - A target's type must match its backend's type. The CLI checks this before Remote does. - An invalid TOML file imports nothing and stops Remote with the target named. Fixing the file and restarting imports again. A TOML target cannot name a backend. - Modify changes only the flags given, and is refused if the target changed since it was read. - Changing one field of an inherited list entry, such as a storage class, keeps its other fields. - --backend= keeps the inherited values as the target's own. Remote fills them in from the backend's definition it holds when it applies the change, not from what the CLI read. A value the same modify clears falls back to the backend's value, as it would with the backend kept. - Moving a target's objects while unfinished jobs use it needs --force. - Delete needs --force. Unfinished jobs are cancelled and undone as Remote next meets them. The delete reason is at most 1024 bytes, with no control characters, as is a job cancel message. - A deleted ID is reused only with `create --id --force`. When the highest ID is taken, the next free ID is the lowest one never used. - A list entry is inherited whole. A backend's entries must be complete. - Deleting a backend is refused while a target names it. So a delete never changes a target. - `backend create|modify --verify` checks the new definition from Remote before storing it, and stores nothing when the store cannot be reached or rejects the keys. - `backend verify` checks a stored backend from Remote and from every sync node, and exits non-zero when a connected host fails. For S3 it lists at most one bucket. Keys that may not list buckets still pass, because the store checked them before it refused. A sync node that Remote still counts as connected but cannot reach is reported as not connected, not as a failed check. - job.rst-db defaults to rst.badger in the directory of job.path-db. The shipped TOML no longer sets it, so an older Remote still starts with that file, and it documents how to roll back. - A sync node that rejects a change starts no work until it accepts one. That includes a change it could not apply because it could not reach Remote. Sync nodes must be upgraded before Remote. - A change waits for a sync node that is connecting, so its push reaches the node. - `list --deleted` and `job cancel --message` require a Remote with target management, rather than silently doing nothing against an older one. - Every new command honors --output. - `beegfs remote list` keeps the output of earlier releases, because scripts parse it. It has the same columns, and policies stay text in JSON. `beegfs remote target list` has the new layout. Live changes also needed these fixes. A builder job whose target is deleted while it runs fails as a precondition instead of crashing sync. A sync node rebuilds its connection to Remote after a failed attempt. A deleted target stays findable through its retired provider while its delete applies. A target delete counts its unfinished jobs before it takes the lock that orders target changes. The protobuf dependency moves to 2c1f61d, which adds the RPCs for targets and backends. This does not: - Find stubs that name a deleted target. - Let a target clear a value its backend sets. - Let TOML define backends. - Require region on a target that names no backend. Such a target still depends on each host's environment, as before. - Keep a backend change made between a modify's read and its write, when the modify changes an inherited list entry. The modify copies the entry the CLI read. - Order job submission with a change. Remote applies a change to itself before it pushes it to each sync node. Work submitted in between can run on a node that still holds the old set. A new target then fails there as unknown. After a bucket or endpoint change, that node uses the old location, as a job run just before the change would. Tests cover each rule above in common/rst, rstdb, the job manager, the worker manager, the sync work manager and server, and the CLI backend and output. Verify is tested against a local HTTP server that answers like an S3 store. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
swartzn
force-pushed
the
swartzn/feat/cli-managed-remote-storage-targets
branch
2 times, most recently
from
November 9, 2026 13:12
32259a5 to
78d2dbe
Compare
A backend holds the connection to a remote store, but its buckets still had to be made with another tool. That tool needed its own copy of the endpoint and the keys, on a host that can reach the store. Add `beegfs remote bucket list|create|modify|delete`, with the alias `container`. Each command names a backend with --backend. Remote runs every request against the store with that backend's settings. So the CLI host needs no route to the store and no keys. Each RST type manages its buckets through an rst.BucketManager, returned by TargetType.Buckets. S3 and xtreemstore share the S3 one. Rules: - create and modify set versioning (--versioning) and tags (--tag). modify can also remove tags (--remove-tag, --clear-tags). A store cannot turn versioning off once it was enabled. - create --create-target also creates a target that names the backend and sets only the bucket. If the bucket is created but the target is not, the bucket stays and the command fails. - list shows the buckets of every backend, or of one with --backend. --verbose adds versioning and tags. A backend whose store cannot be listed makes the command exit non-zero. A bucket whose versioning or tags cannot be read is still listed, with "-" for them, and Remote logs why. - create and modify do not fail when the bucket cannot be read back after the change. A retry of the create would only answer AlreadyExists. The details then show "-", and Remote logs why. - A store that does not implement bucket tagging has buckets without tags. - delete refuses a bucket that a live target uses, with or without --force. A target uses a bucket when its effective configuration names that bucket in the backend's store. Targets of every type with an S3 part count, so an xtreemstore target can use an S3 backend's bucket. - Two endpoints reach the same store when they differ only in host case, a default port, a trailing slash, or the bucket as the first label of the host. An endpoint that cannot be parsed counts as the same store, so the delete is refused. - delete refuses a bucket that holds objects, old versions or unfinished uploads, unless --force is given. With --force, Remote empties the bucket first and reports what it removed. - Emptying runs without the lock that orders target changes, because it can take long. While a delete runs, Remote refuses any target change that would point a target at that bucket. The final delete takes the lock and checks the targets again. - Every new command honors --output. This does not: - Check deleted targets. Their stubs may still name the bucket, and deleting it strands them. - Match one store reached through two different host names, such as an alias in DNS. - Store anything in rstdb, or push anything to sync nodes. The protobuf dependency moves to e5223df, which adds the bucket RPCs. Tests cover the S3 bucket manager against a fake S3 API, including endpoint spellings and details that cannot be read. They also cover the delete rules and the change lock in the job manager, a read back that fails after a change, and the CLI flag parsing. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Every S3 provider built its own HTTP client, and so its own connection pool. Remote and the sync nodes now rebuild a target's provider whenever the target changes, and a change to a backend rebuilds the provider of every target that names it. Each rebuild dropped the warm connections to the endpoint, so the next requests paid for new TCP and TLS handshakes. A credential rotation on a backend did that for every bucket that shares it. All S3 clients now use one HTTP client, built once per process. Sharing is safe for any mix of targets: the transport keys idle connections by scheme, host, port and proxy, so different endpoints never share a connection, and the SDK signs each request, so credentials are not tied to one. - The shared client is built through the same SDK steps each provider's client went through, so it gets the root CAs from AWS_CA_BUNDLE and the timeouts from AWS_DEFAULTS_MODE. - It is assigned in an s3.New option, in newS3AWSClient, which providers and backend verify share. In a defaults mode other than "legacy", s3.New copies a client passed through aws.Config, and each copy builds its own transport. - Idle connections are no longer capped across endpoints. Per endpoint they are capped at the same GOMAXPROCS-scaled number as before, now for all targets on that endpoint together. The environment is read once, so a change to AWS_CA_BUNDLE or AWS_DEFAULTS_MODE needs a restart. With AWS_DEFAULTS_MODE=auto, every target gets the "standard" timeouts, because the shared client has no region to compare with the host's. A test rebuilds a provider with rotated credentials against a local server, under the defaults mode that would copy the client, and checks that both providers used one connection. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
swartzn
force-pushed
the
swartzn/feat/cli-managed-remote-storage-targets
branch
from
November 9, 2026 19:28
78d2dbe to
5e149ed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do / why do we need it?
Remote storage targets (RSTs) could only be defined in the
beegfs-remoteTOML file. Changing oneneeded a restart of Remote and every sync node. Targets that shared a store had to repeat its
endpoint and credentials. Buckets had to be made with another tool, on a host with a route to the
store and a copy of its keys.
This PR adds:
beegfs remote target create|modify|delete|list. Remote stores the definitions in a newBadgerDB (
job.rst-db) and applies each change live, to itself and to every sync node. On firststart it imports the TOML targets. After that the database is the only source.
beegfs remote backend create|modify|delete|list|verify. A backend isthe named connection to one remote store, such as an S3 endpoint and its credentials. A target
of the same type names it with
--backendand inherits every setting it leaves unset. A changeto a backend, such as a credential rotation, reaches all its targets live.
beegfs remote bucket list|create|modify|delete, with the aliascontainer(secondcommit). Remote runs each request against the store with a backend's settings. So the CLI host
needs no route to the store and no keys.
rst.TargetType. Each RST type describes its own configuration, including which settings abackend must and must not set. The CLI, Remote and
rst.Newuse it instead of switching on thetype. A new RST type needs no CLI or Remote code.
and each rebuild used to drop the warm connections to the endpoint.
Behaviour:
it must have every required setting, and a provider must build from it. The name is optional.
modifychanges only the flags given, and is refused if the target changed since it was read.--backend=detaches a target and keeps what it inherited as its own values.needs
--force. This includes a backend change that moves its targets.deleteneeds--force. Unfinished jobs are cancelled and undone as Remote next meets them. Adeleted ID is reused only with
create --id=<id> --force.backend create|modify --verifychecks a new definition from Remote before storing it.backend verifychecks a stored backend from Remote and from every sync node.bucket deleterefuses a bucket that a live target uses, even with--force. Targets of everyS3-based type count. Two endpoints that differ only in host case, a default port, a trailing
slash or a virtual-hosted bucket count as the same store. A populated bucket needs
--force,which empties it first.
bucket create|modifysucceed when the change succeeded, even if Remote cannot read the bucketback afterwards. Details Remote cannot read show as
-, and Remote logs why.remote target list --deletedshows deleted targets.remote job cancelgains--message.remote listkeeps the output of earlier releases, because scripts parse it.remote target listhas the new layout: one column per setting, and what each target inherits.Compatibility:
rst-live-updatefrom everynode. The CLI needs
rst-managementfrom Remote to change targets, and says so against an olderRemote.
remote liststill works there.endpoint-url. Before, each node could pick its ownendpoint from its environment or the region, so nodes could disagree. A TOML file that relied on
this stops Remote at the upgrade, naming the target. The shipped TOML explains the fix.
job.rst-dbdefaults torst.badgernext tojob.path-db. The shipped TOML documents rollback.Related Issue(s)
e5223df)Where should the reviewer(s) start reviewing this?
common/rst/targettype.go, thens3type.goandxtreemstoretype.go.common/rst/backend.go: resolving a target from its backend, and what a backend must set.common/rst/store.go:ValidateTarget, andClientStore.UpdateConfigapplying a diff.rst/remote/internal/job/rstconfig.go: create, update, delete and verify for targets andbackends.
rst/remote/internal/rstdb/: the database and the TOML import.rst/remote/internal/workermgr/rsts.go: applying a set and pushing it to sync nodes.rst/remote/internal/job/rstlookup.go: jobs of a deleted target.rst/sync/internal/workmgr/: the pause after a rejected set.common/rst/bucket.goands3bucket.go, thenrst/remote/internal/job/rstbucket.go: bucketmanagement and the delete rules.
ctl/internal/cmd/rst/,ctl/pkg/ctl/rst/target.goandbucket.go: the CLI.common/rst/s3.go: the shared HTTP client.Are there any specific topics we should discuss before merging?
first, then pushes it to each node, usually within milliseconds. A job in that window can run
with the old set. A new target fails as unknown. After a bucket change, the object may land in
the old bucket, as if the job ran just before the change. No data is lost. A per-node gate in
Remote would close the window. Is it worth it now?
RPCs, but this PR raises the stakes. Any host that can reach Remote can change targets and
backends, read backend secrets, or delete a bucket with its objects. Any host that can reach a
sync node can replace its targets, or make it contact any address through
VerifyBackend.Until that is fixed, these ports must be limited to admin and BeeGFS hosts.
the retired provider instead?
deleted or has its ID reused. A job submitted during the scan is not counted.
undo fails.
bucket deletedoes not check deleted targets. Their stubs may still name the bucket, anddeleting it strands them.
deleteconfirms with--force, while some other commands use--yes.What are the next steps after this PR?
bucket deletecould then check too.Checklist before merging:
CLAUDE.md, andbeegfs-remote.toml.endpoint-urlrequirement and the upgrade order.sharing one connection. Backend verify runs against a local HTTP server that answers like
S3. Bucket management runs against a fake S3 API.
Remote restart, a delete during a builder walk, an offline node reconnecting, a backend
change while jobs run, and the bucket commands against a real store.
HTTP client.
🤖 Generated with Claude Code