Skip to content

Conversation

@nbbeeken
Copy link
Collaborator

@nbbeeken nbbeeken commented Nov 20, 2025

Description

https://jira.mongodb.org/browse/COMPASS-9793

Using devtools connect so we can pull in fast fail logic and other benefits

mongodb-js/devtools-shared#592

Checklist

  • New tests and/or benchmarks are included
  • Documentation is changed or added
  • If this change updates the UI, screenshots/videos are added and a design review is requested
  • If this change could impact the load on the MongoDB cluster, please describe the expected and worst case impact
  • I have signed the MongoDB Contributor License Agreement (https://www.mongodb.com/legal/contributor-agreement)

Motivation and Context

  • Bugfix
  • New feature
  • Dependency update
  • Misc

Open Questions

Dependents

Types of changes

  • Backport Needed
  • Patch (non-breaking change which fixes an issue)
  • Minor (non-breaking change which adds functionality)
  • Major (fix or feature that would cause existing functionality to change)

@nbbeeken nbbeeken force-pushed the COMPASS-9793 branch 2 times, most recently from f5b05e5 to 823c330 Compare November 21, 2025 21:30
Comment on lines -159 to +163
setTimeout(() => {
socket.connect(options);
});
socket.connect(options);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was making an exception uncatchable by the try/catch wrapping connect(). The exception was momentary while I was debugging stuff so not a code path used in practice, but curious to know why we put this in a setTimeout?

},
destroy() {
return Promise.resolve();
// eslint-disable-next-line @typescript-eslint/require-await
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What is the motive for preferring synchronous functions that return a Promise vs using the keyword async that produces the same thing more succinctly?

Happy to put this back to Promise.resove/reject

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you just return a promise then you don't need to add eslint exceptions that you added here and an explanation for why you break the rule (that is missing 🙂). Less unexplained lint exceptions in the code is better than more unexplained lint exceptions

Copy link
Collaborator

Choose a reason for hiding this comment

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

Well I have to agree that it's pretty annoying that eslint doesn't understand that "This function is async because not returning a Promise would break the API contract" even when it could infer that through types

Copy link
Collaborator

Choose a reason for hiding this comment

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

It should in theory based on the docs, we can look more into why it doesn't work for us. Here specifically there are also no types for it to understand that there's an expected promise return

I will still say though that personally I find that at a glance explicit promise return seems clearer than async funciton with no await inside. Disabling eslint rules is also absolutely fine, but annotation should always be provided to make it clear why it's happening, similar the to rule we have for ts-expect-error

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This function is async because not returning a Promise would break the API contract

Basically this trap is a little too easy to fall into esp with functions that are just for throwing. The systemCA function in the other file was synchronously throwing its real counter part is an async function.

In this case, making this misstep is more about missing TS, but generally throwing the keyword around is a way to not think about what you do inside the scope of the function but always get back the return type you want (a promise).


Anyway with the refactors made I think I removed any usage of this eslint exception. Fun fact async () => Promise.resolve(...) doesn't yell at you to await something 😅 best of both worlds??

Comment on lines 21 to 22
const error = new Error('system CA access not available in compass-web');
(error as unknown as { code: 'OUT_OF_MEM' }).code = 'OUT_OF_MEM'; // This is a "tls" error code that makes devtools-connect not use the systemCA
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a bit hacky, but arguably quite stable. Alternatively we can make useSystemCA configurable from the outside.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we're planning on using all of the devtools-connect directly, we should make devtools-connect configurable enough for us to deal with these things without relying on some internal logic of it. I don't think it's a good idea to rely on the error code to force devtools-connect to try connection flow twice

Comment on lines +30 to +31
// WS does not support callback lookup
if ((lookup?.length ?? 0) > 0) lookup = undefined;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is a little inconvenient that this synchronous for CompassWeb and async everywhere else even if it is returning a dummy value. Should we refactor to return the cluster info in a callback?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We are deliberatly not defining this function in a way that would mess up with the original interface as in theory it should be still passed down and be callable by the internals that we don't control and behave in a way that is matching the net interface

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That totally makes sense. Should it throw then if it's given the interface it doesn't support? or more accurately invoke the given callback with an error? any callers will probably hang if we don't do something with it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point, we should probably make the implementation in the connection storage for web fully matching the interface and call a callback with an error

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we're calling the original package directly and still have to stub a bunch of its dependencies for it not to blow up in web, why do we need this local polyfill still?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I thought I needed the indirection still to fix the systemCA thing but if we merge the new option then we should be good once that's passing through.

Comment on lines 1 to 2
import * as devtools_connect from 'devtools-connect-original';
import { createMongoDBOIDCPlugin } from '../oidc-plugin';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Related to https://github.com/mongodb-js/compass/pull/7588/files#r2556225208: this seems redundant, devtools connect will import the polyfill that you provided

return Promise.resolve();
// eslint-disable-next-line @typescript-eslint/require-await
async getStateShareServer() {
return 'Not Available';
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know I wrote this code, but maybe we can throw here or return e.g. null instead? Or otherwise add a comment that this happens to be a return value that "properly" indicates the N/A state because it happens to not be valid JSON? (esp. because the fact that this value needs to be JSON in order to be valid is an implementation detail of devtools-connect)

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.

4 participants