(client: MongoClient, operation: T, timeoutContext?: TimeoutContext | null)
| 63 | * @param operation - The operation to execute |
| 64 | */ |
| 65 | export async function executeOperation< |
| 66 | T extends AbstractOperation, |
| 67 | TResult = ResultTypeFromOperation<T> |
| 68 | >(client: MongoClient, operation: T, timeoutContext?: TimeoutContext | null): Promise<TResult> { |
| 69 | if (!(operation instanceof AbstractOperation)) { |
| 70 | // TODO(NODE-3483): Extend MongoRuntimeError |
| 71 | throw new MongoRuntimeError('This method requires a valid operation instance'); |
| 72 | } |
| 73 | |
| 74 | const topology = |
| 75 | client.topology == null |
| 76 | ? await abortable(autoConnect(client), operation.options) |
| 77 | : client.topology; |
| 78 | |
| 79 | // The driver sessions spec mandates that we implicitly create sessions for operations |
| 80 | // that are not explicitly provided with a session. |
| 81 | let session = operation.session; |
| 82 | let owner: symbol | undefined; |
| 83 | |
| 84 | if (session == null) { |
| 85 | owner = Symbol(); |
| 86 | session = client.startSession({ owner, explicit: false }); |
| 87 | } else if (session.hasEnded) { |
| 88 | throw new MongoExpiredSessionError('Use of expired sessions is not permitted'); |
| 89 | } else if ( |
| 90 | session.snapshotEnabled && |
| 91 | maxWireVersion(topology) < MIN_SUPPORTED_SNAPSHOT_READS_WIRE_VERSION |
| 92 | ) { |
| 93 | throw new MongoCompatibilityError('Snapshot reads require MongoDB 5.0 or later'); |
| 94 | } else if (session.client !== client) { |
| 95 | throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient'); |
| 96 | } |
| 97 | |
| 98 | operation.session ??= session; |
| 99 | |
| 100 | const readPreference = operation.readPreference ?? ReadPreference.primary; |
| 101 | const inTransaction = !!session?.inTransaction(); |
| 102 | |
| 103 | const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION); |
| 104 | |
| 105 | if ( |
| 106 | inTransaction && |
| 107 | !readPreference.equals(ReadPreference.primary) && |
| 108 | (hasReadAspect || operation.commandName === 'runCommand') |
| 109 | ) { |
| 110 | throw new MongoTransactionError( |
| 111 | `Read preference in a transaction must be primary, not: ${readPreference.mode}` |
| 112 | ); |
| 113 | } |
| 114 | |
| 115 | if (session?.isPinned && session.transaction.isCommitted && !operation.bypassPinningCheck) { |
| 116 | session.unpin(); |
| 117 | } |
| 118 | |
| 119 | timeoutContext ??= TimeoutContext.create({ |
| 120 | session, |
| 121 | serverSelectionTimeoutMS: client.s.options.serverSelectionTimeoutMS, |
| 122 | waitQueueTimeoutMS: client.s.options.waitQueueTimeoutMS, |
no test coverage detected