Skip to content

feat(NODE-4521)!: remove custom promise library support #3498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions etc/notes/CHANGES_5.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ npm install --save snappy@7
### Minimum supported Node version

The new minimum supported Node.js version is now 14.20.1.

### Custom Promise library support removed

The MongoClient option `promiseLibrary` along with the `Promise.set` export that allows specifying a custom promise library has been removed.
This allows the driver to adopt async/await syntax which has [performance benefits](https://v8.dev/blog/fast-async) over manual promise construction.
13 changes: 0 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
"@types/whatwg-url": "^11.0.0",
"@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.40.0",
"bluebird": "^3.7.2",
"chai": "^4.3.6",
"chai-subset": "^1.6.0",
"chalk": "^4.1.2",
Expand Down
9 changes: 0 additions & 9 deletions src/connection_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
ServerApiVersion
} from './mongo_client';
import { MongoLogger, MongoLoggerEnvOptions, MongoLoggerMongoClientOptions } from './mongo_logger';
import { PromiseProvider } from './promise_provider';
import { ReadConcern, ReadConcernLevel } from './read_concern';
import { ReadPreference, ReadPreferenceMode } from './read_preference';
import type { TagSet } from './sdam/server_description';
Expand Down Expand Up @@ -431,10 +430,6 @@ export function parseOptions(
mongoOptions.dbName = 'test';
}

if (options.promiseLibrary) {
PromiseProvider.set(options.promiseLibrary);
}

validateLoadBalancedOptions(hosts, mongoOptions, isSRV);

if (mongoClient && mongoOptions.autoEncryption) {
Expand Down Expand Up @@ -960,10 +955,6 @@ export const OPTIONS = {
);
}
},
promiseLibrary: {
deprecated: true,
type: 'any'
},
promoteBuffers: {
type: 'boolean'
},
Expand Down
54 changes: 21 additions & 33 deletions src/cursor/abstract_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { TODO_NODE_3286, TypedEventEmitter } from '../mongo_types';
import { executeOperation, ExecutionResult } from '../operations/execute_operation';
import { GetMoreOperation } from '../operations/get_more';
import { KillCursorsOperation } from '../operations/kill_cursors';
import { PromiseProvider } from '../promise_provider';
import { ReadConcern, ReadConcernLike } from '../read_concern';
import { ReadPreference, ReadPreferenceLike } from '../read_preference';
import type { Server } from '../sdam/server';
Expand Down Expand Up @@ -297,47 +296,36 @@ export abstract class AbstractCursor<
return bufferedDocs;
}

[Symbol.asyncIterator](): AsyncIterator<TSchema, void> {
async function* nativeAsyncIterator(this: AbstractCursor<TSchema>) {
if (this.closed) {
return;
}
async *[Symbol.asyncIterator](): AsyncIterator<TSchema, void> {
if (this.closed) {
return;
}

while (true) {
const document = await this.next();
while (true) {
const document = await this.next();

// Intentional strict null check, because users can map cursors to falsey values.
// We allow mapping to all values except for null.
// eslint-disable-next-line no-restricted-syntax
if (document === null) {
if (!this.closed) {
const message =
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';
// Intentional strict null check, because users can map cursors to falsey values.
// We allow mapping to all values except for null.
// eslint-disable-next-line no-restricted-syntax
if (document === null) {
if (!this.closed) {
const message =
'Cursor returned a `null` document, but the cursor is not exhausted. Mapping documents to `null` is not supported in the cursor transform.';

await cleanupCursorAsync(this, { needsToEmitClosed: true }).catch(() => null);
await cleanupCursorAsync(this, { needsToEmitClosed: true }).catch(() => null);

throw new MongoAPIError(message);
}
break;
}

yield document;

if (this[kId] === Long.ZERO) {
// Cursor exhausted
break;
throw new MongoAPIError(message);
}
break;
}
}

const iterator = nativeAsyncIterator.call(this);
yield document;

if (PromiseProvider.get() == null) {
return iterator;
if (this[kId] === Long.ZERO) {
// Cursor exhausted
break;
}
}
return {
next: () => maybeCallback(() => iterator.next(), null)
};
}

stream(options?: CursorStreamOptions): Readable & AsyncIterable<TSchema> {
Expand Down
4 changes: 0 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { GridFSBucketWriteStream } from './gridfs/upload';
import { Logger } from './logger';
import { MongoClient } from './mongo_client';
import { CancellationToken } from './mongo_types';
import { PromiseProvider } from './promise_provider';
import { ClientSession } from './sessions';

/** @internal */
Expand Down Expand Up @@ -100,9 +99,6 @@ export {
UnorderedBulkOperation
};

// Deprecated, remove in next major
export { PromiseProvider as Promise };

// enums
export { BatchType } from './bulk/common';
export { GSSAPICanonicalizationValue } from './cmap/auth/gssapi';
Expand Down
6 changes: 0 additions & 6 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,6 @@ export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeC
forceServerObjectId?: boolean;
/** A primary key factory function for generation of custom `_id` keys */
pkFactory?: PkFactory;
/**
* A Promise library class the application wishes to use such as Bluebird, must be ES6 compatible
* @deprecated Setting a custom promise library is deprecated the next major version will use the global Promise constructor only.
*/
promiseLibrary?: any;
/** The logging level */
loggerLevel?: LegacyLoggerLevel;
/** Custom logger object */
Expand Down Expand Up @@ -743,7 +738,6 @@ export interface MongoOptions
| 'monitorCommands'
| 'noDelay'
| 'pkFactory'
| 'promiseLibrary'
| 'raw'
| 'replicaSet'
| 'retryReads'
Expand Down
56 changes: 0 additions & 56 deletions src/promise_provider.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import type { MongoClient, MongoOptions } from './mongo_client';
import { TypedEventEmitter } from './mongo_types';
import { executeOperation } from './operations/execute_operation';
import { RunAdminCommandOperation } from './operations/run_command';
import { PromiseProvider } from './promise_provider';
import { ReadConcernLevel } from './read_concern';
import { ReadPreference } from './read_preference';
import { _advanceClusterTime, ClusterTime, TopologyType } from './sdam/common';
Expand Down Expand Up @@ -605,8 +604,7 @@ function attemptTransaction<TSchema>(
try {
promise = fn(session);
} catch (err) {
const PromiseConstructor = PromiseProvider.get() ?? Promise;
promise = PromiseConstructor.reject(err);
promise = Promise.reject(err);
}

if (!isPromiseLike(promise)) {
Expand Down
11 changes: 1 addition & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { Explain } from './explain';
import type { MongoClient } from './mongo_client';
import type { CommandOperationOptions, OperationParent } from './operations/command';
import type { Hint, OperationOptions } from './operations/operation';
import { PromiseProvider } from './promise_provider';
import { ReadConcern } from './read_concern';
import { ReadPreference } from './read_preference';
import { ServerType } from './sdam/common';
Expand Down Expand Up @@ -445,17 +444,9 @@ export function maybeCallback<T>(
promiseFn: () => Promise<T>,
callback?: Callback<T> | null
): Promise<T> | void {
const PromiseConstructor = PromiseProvider.get();

const promise = promiseFn();
if (callback == null) {
if (PromiseConstructor == null) {
return promise;
} else {
return new PromiseConstructor((resolve, reject) => {
promise.then(resolve, reject);
});
}
return promise;
}

promise.then(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { assert as test, setupDatabase } from '../shared';

class CustomPromise extends Promise<void> {}
// @ts-expect-error: Dynamic addition to detect custom promise
CustomPromise.prototype.isCustomMongo = true;

describe('Collection Management and Db Management (promise tests)', function () {
before(function () {
return setupDatabase(this.configuration);
Expand Down
3 changes: 0 additions & 3 deletions test/integration/crud/promise_stats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
const { assert: test, setupDatabase } = require('../shared');
const f = require('util').format;

class CustomPromise extends Promise {}
CustomPromise.prototype.isCustomMongo = true;

describe('stats', function () {
before(function () {
return setupDatabase(this.configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ const { assert: test, setupDatabase } = require('../shared');
const f = require('util').format;
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');

class CustomPromise extends Promise {}
CustomPromise.prototype.isCustomMongo = true;

describe('Handshake', function () {
before(function () {
return setupDatabase(this.configuration);
Expand Down
57 changes: 0 additions & 57 deletions test/integration/node-specific/cursor_async_iterator.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
'use strict';

const { expect } = require('chai');
const Sinon = require('sinon');
const { Promise: BluebirdPromise } = require('bluebird');
const { PromiseProvider } = require('../../../src/promise_provider');

describe('Cursor Async Iterator Tests', function () {
context('default promise library', function () {
Expand Down Expand Up @@ -83,58 +80,4 @@ describe('Cursor Async Iterator Tests', function () {
expect(count).to.equal(1);
});
});
context('custom promise library', () => {
let client, collection, promiseSpy;
beforeEach(async function () {
promiseSpy = Sinon.spy(BluebirdPromise.prototype, 'then');
client = this.configuration.newClient({}, { promiseLibrary: BluebirdPromise });

const connectPromise = client.connect();
expect(connectPromise).to.be.instanceOf(BluebirdPromise);
await connectPromise;
const docs = Array.from({ length: 1 }).map((_, index) => ({ foo: index, bar: 1 }));

collection = client.db(this.configuration.db).collection('async_cursor_tests');

await collection.deleteMany({});
await collection.insertMany(docs);
await client.close();
});

beforeEach(async function () {
client = this.configuration.newClient();
await client.connect();
collection = client.db(this.configuration.db).collection('async_cursor_tests');
});

afterEach(() => {
promiseSpy.restore();
PromiseProvider.set(null);
return client.close();
});

it('should properly use custom promise', async function () {
const cursor = collection.find();
const countBeforeIteration = promiseSpy.callCount;
for await (const doc of cursor) {
expect(doc).to.exist;
}
expect(countBeforeIteration).to.not.equal(promiseSpy.callCount);
expect(promiseSpy.called).to.equal(true);
});

it('should properly use custom promise manual iteration', async function () {
const cursor = collection.find();

const iterator = cursor[Symbol.asyncIterator]();
let isDone;
do {
const promiseFromIterator = iterator.next();
expect(promiseFromIterator).to.be.instanceOf(BluebirdPromise);
const { done, value } = await promiseFromIterator;
if (done) expect(value).to.be.a('undefined');
isDone = done;
} while (!isDone);
});
});
});
Loading