void DBClientSafeWriter::safeWrite( DBClientBase* conn, const BatchItemRef& itemRef, LastError* error ) { const BatchedCommandRequest* request = itemRef.getRequest(); try { // Default settings for checkShardVersion const bool authoritative = false; const int tryNum = 1; // We need to set our version using setShardVersion, managed by checkShardVersionCB versionManager.checkShardVersionCB( conn, request->getTargetingNS(), authoritative, tryNum ); if ( request->getBatchType() == BatchedCommandRequest::BatchType_Insert ) { conn->insert( request->getNS(), request->getInsertRequest()->getDocumentsAt( itemRef.getItemIndex() ), 0 ); } else if ( request->getBatchType() == BatchedCommandRequest::BatchType_Update ) { const BatchedUpdateDocument* update = request->getUpdateRequest()->getUpdatesAt( itemRef.getItemIndex() ); conn->update( request->getNS(), update->getQuery(), update->getUpdateExpr(), update->getUpsert(), update->getMulti() ); } else { dassert( request->getBatchType() == BatchedCommandRequest::BatchType_Delete ); const BatchedDeleteDocument* deleteDoc = request->getDeleteRequest()->getDeletesAt( itemRef.getItemIndex() ); conn->remove( request->getNS(), deleteDoc->getQuery(), deleteDoc->getLimit() == 1 /*just one*/); } // Default GLE Options const bool fsync = false; const bool j = false; const int w = 1; const int wtimeout = 0; BSONObj result = conn->getLastErrorDetailed( NamespaceString( request->getNS() ).db() .toString(), fsync, j, w, wtimeout ); SafeWriter::fillLastError( result, error ); } catch ( const DBException& ex ) { error->raiseError( ex.getCode(), ex.toString().c_str() ); } }
Status DBClientSafeWriter::safeWrite( DBClientBase* conn, const BatchItemRef& itemRef, const BSONObj& writeConcern, BSONObj* gleResponse ) { const BatchedCommandRequest* request = itemRef.getRequest(); try { // Default settings for checkShardVersion const bool authoritative = false; const int tryNum = 1; // We need to set our version using setShardVersion, managed by checkShardVersionCB versionManager.checkShardVersionCB( conn, request->getTargetingNS(), authoritative, tryNum ); if ( request->getBatchType() == BatchedCommandRequest::BatchType_Insert ) { conn->insert( request->getNS(), request->getInsertRequest()->getDocumentsAt( itemRef.getItemIndex() ), 0 ); } else if ( request->getBatchType() == BatchedCommandRequest::BatchType_Update ) { const BatchedUpdateDocument* update = request->getUpdateRequest()->getUpdatesAt( itemRef.getItemIndex() ); conn->update( request->getNS(), update->getQuery(), update->getUpdateExpr(), update->getUpsert(), update->getMulti() ); } else { dassert( request->getBatchType() == BatchedCommandRequest::BatchType_Delete ); const BatchedDeleteDocument* deleteDoc = request->getDeleteRequest()->getDeletesAt( itemRef.getItemIndex() ); conn->remove( request->getNS(), deleteDoc->getQuery(), deleteDoc->getLimit() == 1 /*just one*/); } const StringData& dbName = NamespaceString( request->getNS() ).db(); BSONObjBuilder gleCmdB; gleCmdB.append( "getLastError", true ); gleCmdB.appendElements( writeConcern ); conn->runCommand( dbName.toString(), gleCmdB.obj(), *gleResponse ); } catch ( const DBException& ex ) { return ex.toStatus(); } return Status::OK(); }
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem, BSONObj* upsertedId, WriteErrorDetail** error ) { // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, updateItem ) ); incOpStats( updateItem ); WriteOpResult result; WriteUnitOfWork wunit(_txn->recoveryUnit()); multiUpdate( _txn, updateItem, &result ); wunit.commit(); if ( !result.getStats().upsertedID.isEmpty() ) { *upsertedId = result.getStats().upsertedID; } // END CURRENT OP incWriteStats( updateItem, result.getStats(), result.getError(), currentOp.get() ); finishCurrentOp( _txn, _client, currentOp.get(), result.getError() ); if ( result.getError() ) { result.getError()->setIndex( updateItem.getItemIndex() ); *error = result.releaseError(); } }
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem, WriteErrorDetail** error ) { // Removes are similar to updates, but page faults are handled externally // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) ); incOpStats( removeItem ); WriteOpResult result; // NOTE: Deletes will not fault outside the lock once any data has been written PageFaultRetryableSection pageFaultSection; while ( true ) { try { multiRemove( removeItem, &result ); break; } catch (PageFaultException& pfe) { pfe.touch(); invariant(!result.getError()); continue; } fassertFailed(17429); } // END CURRENT OP incWriteStats( removeItem, result.getStats(), result.getError(), currentOp.get() ); finishCurrentOp( _client, currentOp.get(), result.getError() ); if ( result.getError() ) { result.getError()->setIndex( removeItem.getItemIndex() ); *error = result.releaseError(); } }
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem, BSONObj* upsertedId, WriteErrorDetail** error ) { // Updates currently do a lot of the lock management internally const BatchedCommandRequest& request = *updateItem.getRequest(); const NamespaceString nss( updateItem.getRequest()->getNS() ); // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, updateItem ) ); incOpStats( updateItem ); WriteOpResult result; { /////////////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); /////////////////////////////////////////// // Check version once we're locked if ( checkShardVersion( &shardingState, request, &result.error ) ) { // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); multiUpdate( updateItem, &result ); incWriteStats( updateItem, result.stats, result.error, currentOp.get() ); if ( !result.stats.upsertedID.isEmpty() ) { *upsertedId = result.stats.upsertedID.getOwned(); } } } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( updateItem.getItemIndex() ); *error = result.releaseError(); } }
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem, WriteErrorDetail** error ) { // Removes are similar to updates, but page faults are handled externally // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) ); incOpStats( removeItem ); WriteOpResult result; multiRemove( _txn, removeItem, &result ); // END CURRENT OP incWriteStats( removeItem, result.getStats(), result.getError(), currentOp.get() ); finishCurrentOp( _txn, _client, currentOp.get(), result.getError() ); if ( result.getError() ) { result.getError()->setIndex( removeItem.getItemIndex() ); *error = result.releaseError(); } }
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem, WriteErrorDetail** error ) { // Removes are similar to updates, but page faults are handled externally // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) ); incOpStats( removeItem ); WriteOpResult result; while ( true ) { multiRemove( removeItem, &result ); if ( !result.fault ) { incWriteStats( removeItem, result.stats, result.error, currentOp.get() ); break; } // // Check page fault out of lock // dassert( result.fault ); result.fault->touch(); result.reset(); } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( removeItem.getItemIndex() ); *error = result.releaseError(); } }
void WriteBatchExecutor::execUpdate( const BatchItemRef& updateItem, BSONObj* upsertedId, WriteErrorDetail** error ) { // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, updateItem ) ); incOpStats( updateItem ); WriteOpResult result; multiUpdate( updateItem, &result ); incWriteStats( updateItem, result.stats, result.error, currentOp.get() ); if ( !result.stats.upsertedID.isEmpty() ) { *upsertedId = result.stats.upsertedID; } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( updateItem.getItemIndex() ); *error = result.releaseError(); } }
bool WriteBatchExecutor::doWrite( const string& ns, const BatchItemRef& itemRef, CurOp* currentOp, WriteStats* stats, BSONObj* upsertedID, BatchedErrorDetail* error ) { const BatchedCommandRequest& request = *itemRef.getRequest(); int index = itemRef.getItemIndex(); // // Check our shard version if we need to (must be in the write lock) // CollectionMetadataPtr metadata; if ( shardingState.enabled() ) { // Index inserts make the namespace nontrivial for versioning string targetingNS = itemRef.getRequest()->getTargetingNS(); Lock::assertWriteLocked( targetingNS ); metadata = shardingState.getCollectionMetadata( targetingNS ); if ( request.isShardVersionSet() && !ChunkVersion::isIgnoredVersion( request.getShardVersion() ) ) { ChunkVersion shardVersion = metadata ? metadata->getShardVersion() : ChunkVersion::UNSHARDED(); if ( !request.getShardVersion() // .isWriteCompatibleWith( shardVersion ) ) { buildStaleError( request.getShardVersion(), shardVersion, error ); return false; } } } // // Not stale, do the actual write // if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ) { // Need to check for unique index problems if ( metadata && request.isUniqueIndexRequest() ) { if ( !isUniqueIndexCompatible( metadata->getKeyPattern(), request.getIndexKeyPattern() ) ) { buildUniqueIndexError( metadata->getKeyPattern(), request.getIndexKeyPattern(), error ); return false; } } // Insert return doInsert( ns, request.getInsertRequest()->getDocumentsAt( index ), currentOp, stats, error ); } else if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update ) { // TODO: Pass down immutable shard key fields // Update return doUpdate( ns, *request.getUpdateRequest()->getUpdatesAt( index ), currentOp, stats, upsertedID, error ); } else { dassert( request.getBatchType() == BatchedCommandRequest::BatchType_Delete ); // Delete return doDelete( ns, *request.getDeleteRequest()->getDeletesAt( index ), currentOp, stats, error ); } }
bool WriteBatchExecutor::doWrite( const string& ns, const BatchItemRef& itemRef, CurOp* currentOp, WriteStats* stats, BSONObj* upsertedID, BatchedErrorDetail* error ) { const BatchedCommandRequest& request = *itemRef.getRequest(); int index = itemRef.getItemIndex(); // // Check our shard version if we need to (must be in the write lock) // if ( shardingState.enabled() && request.isShardVersionSet() && !ChunkVersion::isIgnoredVersion( request.getShardVersion() ) ) { Lock::assertWriteLocked( ns ); CollectionMetadataPtr metadata = shardingState.getCollectionMetadata( ns ); ChunkVersion shardVersion = metadata ? metadata->getShardVersion() : ChunkVersion::UNSHARDED(); if ( !request.getShardVersion() // .isWriteCompatibleWith( shardVersion ) ) { // Write stale error to results error->setErrCode( ErrorCodes::StaleShardVersion ); BSONObjBuilder infoB; shardVersion.addToBSON( infoB, "vWanted" ); error->setErrInfo( infoB.obj() ); string errMsg = mongoutils::str::stream() << "stale shard version detected before write, received " << request.getShardVersion().toString() << " but local version is " << shardVersion.toString(); error->setErrMessage( errMsg ); return false; } } // // Not stale, do the actual write // if ( request.getBatchType() == BatchedCommandRequest::BatchType_Insert ) { // Insert return doInsert( ns, request.getInsertRequest()->getDocumentsAt( index ), currentOp, stats, error ); } else if ( request.getBatchType() == BatchedCommandRequest::BatchType_Update ) { // Update return doUpdate( ns, *request.getUpdateRequest()->getUpdatesAt( index ), currentOp, stats, upsertedID, error ); } else { dassert( request.getBatchType() == BatchedCommandRequest::BatchType_Delete ); // Delete return doDelete( ns, *request.getDeleteRequest()->getDeletesAt( index ), currentOp, stats, error ); } }
void WriteBatchExecutor::execRemove( const BatchItemRef& removeItem, WriteErrorDetail** error ) { // Removes are similar to updates, but page faults are handled externally const BatchedCommandRequest& request = *removeItem.getRequest(); const NamespaceString nss( removeItem.getRequest()->getNS() ); // BEGIN CURRENT OP scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, removeItem ) ); incOpStats( removeItem ); WriteOpResult result; while ( true ) { { // NOTE: Deletes will not fault outside the lock once any data has been written PageFaultRetryableSection pFaultSection; /////////////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); /////////////////////////////////////////// // Check version once we're locked if ( !checkShardVersion( &shardingState, request, &result.error ) ) { // Version error break; } // Context once we're locked, to set more details in currentOp() // TODO: better constructor? Client::Context writeContext( nss.ns(), storageGlobalParams.dbpath, false /* don't check version */); multiRemove( removeItem, &result ); if ( !result.fault ) { incWriteStats( removeItem, result.stats, result.error, currentOp.get() ); break; } } // // Check page fault out of lock // dassert( result.fault ); result.fault->touch(); result.reset(); } // END CURRENT OP finishCurrentOp( _client, currentOp.get(), result.error ); if ( result.error ) { result.error->setIndex( removeItem.getItemIndex() ); *error = result.releaseError(); } }