static void insertOne(WriteBatchExecutor::ExecInsertsState* state, WriteOpResult* result) { invariant(state->currIndex < state->normalizedInserts.size()); const StatusWith<BSONObj>& normalizedInsert(state->normalizedInserts[state->currIndex]); if (!normalizedInsert.isOK()) { result->setError(toWriteError(normalizedInsert.getStatus())); return; } const BSONObj& insertDoc = normalizedInsert.getValue().isEmpty() ? state->request->getInsertRequest()->getDocumentsAt( state->currIndex ) : normalizedInsert.getValue(); try { if (state->lockAndCheck(result)) { if (!state->request->isInsertIndexRequest()) { singleInsert(state->txn, insertDoc, state->getCollection(), result); } else { singleCreateIndex(state->txn, insertDoc, state->getCollection(), result); } } } catch (const DBException& ex) { Status status(ex.toStatus()); if (ErrorCodes::isInterruption(status.code())) throw; result->setError(toWriteError(status)); } // Errors release the write lock, as a matter of policy. if (result->getError()) state->unlock(); }
static void insertOne(WriteBatchExecutor::ExecInsertsState* state, WriteOpResult* result) { invariant(state->currIndex < state->normalizedInserts.size()); const StatusWith<BSONObj>& normalizedInsert(state->normalizedInserts[state->currIndex]); if (!normalizedInsert.isOK()) { result->setError(toWriteError(normalizedInsert.getStatus())); return; } const BSONObj& insertDoc = normalizedInsert.getValue().isEmpty() ? state->request->getInsertRequest()->getDocumentsAt( state->currIndex ) : normalizedInsert.getValue(); cc().clearHasWrittenThisOperation(); { PageFaultRetryableSection pageFaultSection; while (true) { try { if (!state->lockAndCheck(result)) { break; } if (!state->request->isInsertIndexRequest()) { const PregeneratedKeys* pregen = NULL; if ( state->pregeneratedKeys.size() > state->currIndex ) pregen = &state->pregeneratedKeys[state->currIndex]; singleInsert(insertDoc, state->getCollection(), pregen, result); } else { singleCreateIndex(insertDoc, state->getCollection(), result); } break; } catch (const DBException& ex) { Status status(ex.toStatus()); if (ErrorCodes::isInterruption(status.code())) throw; result->setError(toWriteError(status)); break; } catch (PageFaultException& pfe) { state->unlock(); pfe.touch(); continue; // Try the operation again. } fassertFailed(17430); } } // end PageFaultRetryableSection // Errors release the write lock, as a matter of policy. if (result->getError()) state->unlock(); }
void WriteBatchExecutor::execInserts( const BatchedCommandRequest& request, std::vector<WriteErrorDetail*>* errors ) { // Bulk insert is a bit different from other bulk operations in that multiple request docs // can be processed at once inside the write lock. const NamespaceString nss( request.getTargetingNS() ); scoped_ptr<BatchItemRef> currInsertItem( new BatchItemRef( &request, 0 ) ); // Go through our request and do some preprocessing on insert documents outside the lock to // validate and put them in a normalized form - i.e. put _id in front and fill in // timestamps. The insert document may also be invalid. // TODO: Might be more efficient to do in batches. vector<StatusWith<BSONObj> > normalInserts; normalizeInserts( request, &normalInserts ); while ( currInsertItem->getItemIndex() < static_cast<int>( request.sizeWriteOps() ) ) { WriteOpResult currResult; // Don't (re-)acquire locks and create database until it's necessary if ( !normalInserts[currInsertItem->getItemIndex()].isOK() ) { currResult.error = toWriteError( normalInserts[currInsertItem->getItemIndex()].getStatus() ); } else { PageFaultRetryableSection pFaultSection; //////////////////////////////////// Lock::DBWrite writeLock( nss.ns() ); //////////////////////////////////// // Check version inside of write lock if ( checkIsMasterForCollection( nss, &currResult.error ) && checkShardVersion( &shardingState, request, &currResult.error ) && checkIndexConstraints( &shardingState, request, &currResult.error ) ) { // // Get the collection for the insert // scoped_ptr<Client::Context> writeContext; Collection* collection = NULL; try { // Context once we're locked, to set more details in currentOp() // TODO: better constructor? writeContext.reset( new Client::Context( request.getNS(), storageGlobalParams.dbpath, false /* don't check version */) ); Database* database = writeContext->db(); dassert( database ); collection = database->getCollection( nss.ns() ); if ( !collection ) { // Implicitly create if it doesn't exist collection = database->createCollection( nss.ns() ); if ( !collection ) { currResult.error = toWriteError( Status( ErrorCodes::InternalError, "could not create collection" ) ); } } } catch ( const DBException& ex ) { Status status(ex.toStatus()); if (ErrorCodes::isInterruption(status.code())) { throw; } currResult.error = toWriteError(status); } // // Perform writes inside write lock // while ( collection && currInsertItem->getItemIndex() < static_cast<int>( request.sizeWriteOps() ) ) { // // BEGIN CURRENT OP // scoped_ptr<CurOp> currentOp( beginCurrentOp( _client, *currInsertItem ) ); incOpStats( *currInsertItem ); // Get the actual document we want to write, assuming it's valid const StatusWith<BSONObj>& normalInsert = // normalInserts[currInsertItem->getItemIndex()]; const BSONObj& normalInsertDoc = normalInsert.getValue().isEmpty() ? currInsertItem->getDocument() : normalInsert.getValue(); if ( !normalInsert.isOK() ) { // This insert failed on preprocessing currResult.error = toWriteError( normalInsert.getStatus() ); } else if ( !request.isInsertIndexRequest() ) { // Try the insert singleInsert( *currInsertItem, normalInsertDoc, collection, &currResult ); } else { // Try the create index singleCreateIndex( *currInsertItem, normalInsertDoc, collection, &currResult ); } // // END CURRENT OP // finishCurrentOp( _client, currentOp.get(), currResult.error ); // Faults release the write lock if ( currResult.fault ) break; // In general, we might have stats and errors incWriteStats( *currInsertItem, currResult.stats, currResult.error, currentOp.get() ); // Errors release the write lock if ( currResult.error ) break; // Increment in the write lock and reset the stats for next time currInsertItem.reset( new BatchItemRef( &request, currInsertItem->getItemIndex() + 1 ) ); currResult.reset(); // Destruct curop so that our parent curop is restored, so that we // record the yield count in the parent. currentOp.reset(NULL); // yield sometimes int micros = ClientCursor::suggestYieldMicros(); if (micros > 0) { ClientCursor::staticYield(micros, "", NULL); } } } } // END WRITE LOCK // // Store the current error if it exists // if ( currResult.error ) { errors->push_back( currResult.releaseError() ); errors->back()->setIndex( currInsertItem->getItemIndex() ); // Break early for ordered batches if ( request.getOrdered() ) break; } // // Fault or increment // if ( currResult.fault ) { // Check page fault out of lock currResult.fault->touch(); } else { // Increment if not a fault currInsertItem.reset( new BatchItemRef( &request, currInsertItem->getItemIndex() + 1 ) ); } } }