Skip to content

Commit

Permalink
fix(NODE-5044): Write Concern 0 Must Not Affect Read Operations (#3541)…
Browse files Browse the repository at this point in the history
… (#3575)

Co-authored-by: Warren James <warren.james.dev@gmail.com>
Co-authored-by: Neal Beeken <neal.beeken@mongodb.com>
  • Loading branch information
3 people committed Feb 22, 2023
1 parent 722a4a6 commit 10146a4
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 92 deletions.
14 changes: 11 additions & 3 deletions src/change_stream.ts
Expand Up @@ -82,7 +82,7 @@ export type OperationTime = Timestamp;
* Options that can be passed to a ChangeStream. Note that startAfter, resumeAfter, and startAtOperationTime are all mutually exclusive, and the server will error if more than one is specified.
* @public
*/
export interface ChangeStreamOptions extends AggregateOptions {
export interface ChangeStreamOptions extends Omit<AggregateOptions, 'writeConcern'> {
/**
* Allowed values: 'updateLookup', 'whenAvailable', 'required'.
*
Expand Down Expand Up @@ -533,7 +533,14 @@ export class ChangeStream<
TChange extends Document = ChangeStreamDocument<TSchema>
> extends TypedEventEmitter<ChangeStreamEvents<TSchema, TChange>> {
pipeline: Document[];
options: ChangeStreamOptions;
/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
* key must be present on the options in order to delete it.
* This allows typescript to delete the key but will
* not allow a writeConcern to be assigned as a property on options.
*/
options: ChangeStreamOptions & { writeConcern?: never };
parent: MongoClient | Db | Collection;
namespace: MongoDBNamespace;
type: symbol;
Expand Down Expand Up @@ -586,7 +593,8 @@ export class ChangeStream<
super();

this.pipeline = pipeline;
this.options = options;
this.options = { ...options };
delete this.options.writeConcern;

if (parent instanceof Collection) {
this.type = CHANGE_DOMAIN_TYPES.COLLECTION;
Expand Down
4 changes: 3 additions & 1 deletion src/operations/aggregate.ts
Expand Up @@ -44,7 +44,7 @@ export class AggregateOperation<T = Document> extends CommandOperation<T> {
constructor(ns: MongoDBNamespace, pipeline: Document[], options?: AggregateOptions) {
super(undefined, { ...options, dbName: ns.db });

this.options = options ?? {};
this.options = { ...options };

// Covers when ns.collection is null, undefined or the empty string, use DB_AGGREGATE_COLLECTION
this.target = ns.collection || DB_AGGREGATE_COLLECTION;
Expand All @@ -65,6 +65,8 @@ export class AggregateOperation<T = Document> extends CommandOperation<T> {

if (this.hasWriteStage) {
this.trySecondaryWrite = true;
} else {
delete this.options.writeConcern;
}

if (this.explain && this.writeConcern) {
Expand Down
15 changes: 12 additions & 3 deletions src/operations/find.ts
Expand Up @@ -14,7 +14,8 @@ import { Aspect, defineAspects, Hint } from './operation';
* @typeParam TSchema - Unused schema definition, deprecated usage, only specify `FindOptions` with no generic
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface FindOptions<TSchema extends Document = Document> extends CommandOperationOptions {
export interface FindOptions<TSchema extends Document = Document>
extends Omit<CommandOperationOptions, 'writeConcern'> {
/** Sets the limit of documents returned in the query. */
limit?: number;
/** Set to sort the documents coming back from the query. Array of indexes, `[['a', 1]]` etc. */
Expand Down Expand Up @@ -66,7 +67,14 @@ export interface FindOptions<TSchema extends Document = Document> extends Comman

/** @internal */
export class FindOperation extends CommandOperation<Document> {
override options: FindOptions;
/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
* key must be present on the options in order to delete it.
* This allows typescript to delete the key but will
* not allow a writeConcern to be assigned as a property on options.
*/
override options: FindOptions & { writeConcern?: never };
filter: Document;

constructor(
Expand All @@ -77,7 +85,8 @@ export class FindOperation extends CommandOperation<Document> {
) {
super(collection, options);

this.options = options;
this.options = { ...options };
delete this.options.writeConcern;
this.ns = ns;

if (typeof filter !== 'object' || Array.isArray(filter)) {
Expand Down
16 changes: 12 additions & 4 deletions src/operations/indexes.ts
Expand Up @@ -102,7 +102,7 @@ export interface IndexDescription
}

/** @public */
export interface CreateIndexesOptions extends CommandOperationOptions {
export interface CreateIndexesOptions extends Omit<CommandOperationOptions, 'writeConcern'> {
/** Creates the index in the background, yielding whenever possible. */
background?: boolean;
/** Creates an unique index. */
Expand Down Expand Up @@ -382,20 +382,28 @@ export class DropIndexesOperation extends DropIndexOperation {
}

/** @public */
export interface ListIndexesOptions extends CommandOperationOptions {
export interface ListIndexesOptions extends Omit<CommandOperationOptions, 'writeConcern'> {
/** The batchSize for the returned command cursor or if pre 2.8 the systems batch collection */
batchSize?: number;
}

/** @internal */
export class ListIndexesOperation extends CommandOperation<Document> {
override options: ListIndexesOptions;
/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
* key must be present on the options in order to delete it.
* This allows typescript to delete the key but will
* not allow a writeConcern to be assigned as a property on options.
*/
override options: ListIndexesOptions & { writeConcern?: never };
collectionNamespace: MongoDBNamespace;

constructor(collection: Collection, options?: ListIndexesOptions) {
super(collection, options);

this.options = options ?? {};
this.options = { ...options };
delete this.options.writeConcern;
this.collectionNamespace = collection.s.namespace;
}

Expand Down
14 changes: 11 additions & 3 deletions src/operations/list_collections.ts
Expand Up @@ -7,7 +7,7 @@ import { CommandOperation, CommandOperationOptions } from './command';
import { Aspect, defineAspects } from './operation';

/** @public */
export interface ListCollectionsOptions extends CommandOperationOptions {
export interface ListCollectionsOptions extends Omit<CommandOperationOptions, 'writeConcern'> {
/** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */
nameOnly?: boolean;
/** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */
Expand All @@ -18,7 +18,14 @@ export interface ListCollectionsOptions extends CommandOperationOptions {

/** @internal */
export class ListCollectionsOperation extends CommandOperation<string[]> {
override options: ListCollectionsOptions;
/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
* key must be present on the options in order to delete it.
* This allows typescript to delete the key but will
* not allow a writeConcern to be assigned as a property on options.
*/
override options: ListCollectionsOptions & { writeConcern?: never };
db: Db;
filter: Document;
nameOnly: boolean;
Expand All @@ -28,7 +35,8 @@ export class ListCollectionsOperation extends CommandOperation<string[]> {
constructor(db: Db, filter: Document, options?: ListCollectionsOptions) {
super(db, options);

this.options = options ?? {};
this.options = { ...options };
delete this.options.writeConcern;
this.db = db;
this.filter = filter;
this.nameOnly = !!this.options.nameOnly;
Expand Down
75 changes: 0 additions & 75 deletions test/integration/read-write-concern/write_concern.test.js

This file was deleted.

0 comments on commit 10146a4

Please sign in to comment.