Skip to content

feat: DSC support for mongodb runner#822

Draft
mpobrien wants to merge 5 commits into
mongodb-js:mainfrom
mpobrien:07-01-dsc_support_for_mongodb-runner
Draft

feat: DSC support for mongodb runner#822
mpobrien wants to merge 5 commits into
mongodb-js:mainfrom
mpobrien:07-01-dsc_support_for_mongodb-runner

Conversation

@mpobrien

@mpobrien mpobrien commented Jul 15, 2026

Copy link
Copy Markdown

Adds the ability to launch DSC clusters backed by SLS.

  • New disaggregatedStorage cluster option (all topologies): starts a docker compose project for the storage backend, waits for readiness, runs per-shard setup, and injects disaggregatedStorageConfig/disaggregatedStorageEnabled into every mongod. Compose project is shared across shards and torn down with the cluster (including on failed startup).
  • SLS helpers: createSLSDisaggregatedStorageOptions() handles the full flow for the SLS multi-cell compose file from a server checkout — env/port allocation (services parsed from the compose file itself), readiness polling, per-shard StartLog, encryption key file, and auto-generated disaggregatedStorageConfig (incl. replSetConfig from pre-allocated member ports).
  • DSC-specific behavior: replset defaults to 2 nodes, replSetInitiate is skipped (config comes from startup parameters), and local-db metadata tracking is disabled (not writable on DSC).
  • New CLI flags: --slsCompose/--slsImageTag (full SLS flow), --disaggregatedStorageCompose/--disaggregatedStorageConfig (custom backends), and --downloadUrl for using a direct mongod tarball URL
    (also added to mongodb-downloader, cached by URL).
  • Misc: docker compose output + download progress in debug logs, mongodb-runner start prints allocated SLS service ports.

Sample invocation:

node bin/runner.js start -t replset \
  --slsCompose=/home/ubuntu/mongo/buildscripts/modules/atlas/sls-multicell-docker-compose.yml \
  --slsImageTag=dfb12c1092695ae79f6ff3c5b6c31ff7417e4843 \
  --binDir=/mnt/mac/Users/mikeo/ubuntu-mongod/dist-test/bin/ \
  --debug --logDir=/tmp/mongodb-runner-logs

Open Questions

Checklist

Copilot AI review requested due to automatic review settings July 15, 2026 19:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds disaggregated storage (SLS) support to mongodb-runner, including orchestration of a docker compose-backed storage layer, pre-allocated ports for mongod members, and new CLI/config surface to drive it. It also extends mongodb-downloader to support downloading MongoDB binaries from a direct tarball URL (with progress logging).

Changes:

  • Add disaggregated storage abstractions (DisaggregatedStorageOptions, SLS helpers) and wire them into cluster startup/teardown.
  • Add docker compose lifecycle helper and CLI flags for SLS/disaggregated storage setup.
  • Add downloadUrl support to the MongoDB downloader and runner, enabling custom server builds.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/saslprep/src/code-points-data.ts Updates embedded saslprep code point data blob.
packages/mongodb-runner/src/util.ts Adds allocatePort() helper for pre-allocating member ports.
packages/mongodb-runner/src/util.js Adds JS runtime utility helpers (parallel helpers, safePromiseAll, connection string helpers).
packages/mongodb-runner/src/tls-helpers.js Adds TLS client key material helper for runner-managed TLS setups.
packages/mongodb-runner/src/sls.ts Introduces SLS compose parsing, env/port allocation, readiness, and config generation helpers.
packages/mongodb-runner/src/runner-helpers.js Adds JS runtime helpers for CLI commands (start/stop/prune/exec).
packages/mongodb-runner/src/oidc.js Adds JS runtime OIDC mock provider subprocess management.
packages/mongodb-runner/src/mongoserver.ts Adds disagg detection and skips local-db metadata tracking for disagg servers.
packages/mongodb-runner/src/mongoserver.js Adds JS runtime implementation of MongoServer (process management, metadata, logging).
packages/mongodb-runner/src/mongologreader.js Adds JS runtime log parsing/stream filtering for port/build info extraction.
packages/mongodb-runner/src/mongocluster.ts Wires in disaggregated storage (compose lifecycle, per-shard setup, port preallocation) and downloadUrl.
packages/mongodb-runner/src/mongocluster.spec.js Adds JS test coverage for cluster behaviors and CLI plumbing.
packages/mongodb-runner/src/mongocluster.js Adds JS runtime implementation of MongoCluster including docker compose integration.
packages/mongodb-runner/src/index.ts Exports new disagg/SLS and docker compose APIs from the package entrypoint.
packages/mongodb-runner/src/index.js Adds JS runtime entrypoint exports for main APIs and CLI helpers.
packages/mongodb-runner/src/docker-compose.ts Adds typed docker compose project wrapper (env + project naming + down --volumes).
packages/mongodb-runner/src/docker-compose.js Adds JS runtime docker compose wrapper.
packages/mongodb-runner/src/cli.ts Adds CLI flags for downloadUrl, SLS compose/image tag, and generic disagg compose/config.
packages/mongodb-runner/src/cli.spec.js Adds JS tests for CLI workflows (start/stop/ls/prune/exec/config).
packages/mongodb-runner/src/cli.js Adds JS runtime CLI implementation via yargs.
packages/mongodb-runner/README.md Adds documentation pointer for disaggregated storage clusters.
packages/mongodb-runner/examples/sls-replset.js Adds example for starting an SLS-backed replica set with a custom mongod build.
packages/mongodb-downloader/src/index.ts Adds downloadUrl support, better error handling, and download progress logging.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/mongodb-runner/src/oidc.js Outdated
Comment on lines +1 to +8
import { spawn } from 'child_process';
import { once } from 'events';
import { parseCLIArgs, OIDCMockProvider } from '@mongodb-js/oidc-mock-provider';
import { debug } from './util';
if (process.env.RUN_OIDC_MOCK_PROVIDER !== undefined) {
(async function main() {
const uuid = crypto.randomUUID();
debug('starting OIDC mock provider with UUID', uuid);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecef1cf — the compiled .js files in src/ were committed accidentally and have been removed; the .ts source now imports randomUUID explicitly from 'crypto'.

Comment thread packages/mongodb-runner/src/oidc.js Outdated
return; // process already exited
}
if (!this.issuer || !this.uuid) return;
await fetch(new URL(this.issuer, `/shutdown/${this.uuid}`));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecef1cf — arguments swapped to new URL(/shutdown/${uuid}, this.issuer) in oidc.ts (the stale compiled .js files were removed).

Comment thread packages/mongodb-runner/src/util.js Outdated
Comment on lines +39 to +42
throw new AggregateError(
[rejected.map((r) => r.reason)],
`${rejected.length} errors: ${rejected.map((r) => r.reason).join(', ')}`,
);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecef1cf — the errors array is no longer nested; AggregateError.errors now contains the individual errors.

Comment on lines +610 to +614
if (disaggregatedStorage) {
// Pre-allocate member ports so the storage configuration (which
// embeds the replica set config, including member host:ports) can be
// computed before the servers start.
const members: ShardMemberDescriptor[] = [];

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecef1cfShardMemberDescriptor now carries arbiterOnly, populated from the replica set member options, and the generated DSC replSetConfig includes it for each member.

Comment on lines +159 to +164
isClosed() {
// Return true if and only if there are no running sub-clusters/servers
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const _ of this.children()) return true;
return true;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecef1cfisClosed() now returns false while child servers/shards exist (the stale compiled .js copy was removed).

@mpobrien mpobrien changed the title DSC support for mongodb runner feat: DSC support for mongodb runner Jul 15, 2026
- flatten AggregateError errors array in safePromiseAll
- fix isClosed() to return false while child servers/shards are running
- fix reversed new URL() arguments in OIDC mock provider shutdown
- import randomUUID explicitly in oidc.ts
- include arbiterOnly in ShardMemberDescriptor and generated DSC replSetConfig
- add docs/disaggregated-storage.md

@gribnoysup gribnoysup left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The high level direction this is going looks good to me. I didn't do a thorough review yet as this is a draft, but a couple of things already caught my eye, so I left a couple of comments here and there. Happy to discuss those more

Comment on lines +35 to +39
/**
* A direct URL to a MongoDB tarball to download. If set, `version` and
* `downloadOptions` are ignored and no download URL lookup is performed.
*/
downloadUrl?: string;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should think about some different approach for adding this functionality in the downloader: the issue I see here is that while downloadOptions will be ignored, we will still consider them somewhat when returning the DownloadResult metadata, which from the consumer perspective might put you in a weird state where what you download doesn't match the returned options at all

options: downloadOptions,
});
const artifactInfo = downloadUrl
? ({ url: downloadUrl } as DownloadArtifactInfo)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a dangerous assertion that makes the public interface here return a value that doesn't match the provided type at all and can cause consumer code to crash completely

Comment on lines +141 to +143
if (argv.slsCompose && !argv.slsImageTag) {
throw new Error('--slsCompose requires --slsImageTag');
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the yargs library you can specify these dependencies in the options definition

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, added the implies field in the yargs options to do this instead.

try {
return JSON.parse(argv.disaggregatedStorageConfig ?? '');
} catch {
return argv.disaggregatedStorageConfig ?? '';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this continue if we couldn't parse the arg?

Comment on lines +489 to +495
// Internal parameter used when starting the shards of a sharded cluster:
// the parent cluster owns the docker compose project, and the shards
// inherit the DSC context from it.
_internal?: {
disaggregatedStorage: DisaggregatedStorageOptions;
shardContext: { index: number; isConfigServer: boolean };
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it a bit clunky that we're expressing this logic via an "internal" parameter on a public interface. If the idea here is that we want to know that the MongoCluster is a child of another MongoCluster that might be a disagg one, maybe we can model it that way and just pass a parent cluster as a second param here (and expose whatever we need from the parent so that the child cluster can read it)

cluster,
options,
disaggregatedStorage,
_internal?.shardContext ?? { index: 0, isConfigServer: false },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback value is kinda weird: it looks like it's generic enough to apply to all shards we create, but it's gated by disagg check, so if someone reads it while creating a non-disagg cluster it will actually report state that is contradictory (a config server we're trying to start will read isConfig: false for example). Can we either rename this and make it optional to make sure it's clear it's disagg-only, or just make it generic enough so that the value is correct for all cases?

Comment on lines +86 to +104
.option('slsCompose', {
type: 'string',
describe:
'Path to an SLS multi-cell docker-compose.yml; launches the SLS DSC project and configures mongod to use it (requires a DSC-capable mongod via --binDir or --downloadUrl)',
})
.option('slsImageTag', {
type: 'string',
describe:
'SLS docker image tag to use with --slsCompose (e.g. the pinned_sls_commit from the server repo manifest)',
})
.option('disaggregatedStorageCompose', {
type: 'string',
describe: 'Path to docker-compose.yml for the DSC backend',
})
.option('disaggregatedStorageConfig', {
type: 'string',
describe:
'JSON value for the disaggregatedStorageConfig setParameter on each mongod',
})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you should either provide sls or disaggregated args (based on the logic I see below), maybe worth validating this case aborting if all these are provided?

@mpobrien

Copy link
Copy Markdown
Author

Thanks for the in-depth review @gribnoysup ! Will work through these, I might have a few follow-up questions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants