StatusWith<RecordId> RecordStoreV1Base::_insertRecord(OperationContext* txn, const char* data, int len, bool enforceQuota) { const int lenWHdr = len + MmapV1RecordHeader::HeaderSize; const int lenToAlloc = shouldPadInserts() ? quantizeAllocationSpace(lenWHdr) : lenWHdr; fassert(17208, lenToAlloc >= lenWHdr); StatusWith<DiskLoc> loc = allocRecord(txn, lenToAlloc, enforceQuota); if (!loc.isOK()) return StatusWith<RecordId>(loc.getStatus()); MmapV1RecordHeader* r = recordFor(loc.getValue()); fassert(17210, r->lengthWithHeaders() >= lenWHdr); // copy the data r = reinterpret_cast<MmapV1RecordHeader*>(txn->recoveryUnit()->writingPtr(r, lenWHdr)); memcpy(r->data(), data, len); _addRecordToRecListInExtent(txn, r, loc.getValue()); _details->incrementStats(txn, r->netLength(), 1); return StatusWith<RecordId>(loc.getValue().toRecordId()); }
StatusWith<RecordId> RecordStoreV1Base::insertRecord(OperationContext* txn, const DocWriter* doc, bool enforceQuota) { int docSize = doc->documentSize(); if (docSize < 4) { return StatusWith<RecordId>(ErrorCodes::InvalidLength, "record has to be >= 4 bytes"); } const int lenWHdr = docSize + MmapV1RecordHeader::HeaderSize; if (lenWHdr > MaxAllowedAllocation) { return StatusWith<RecordId>(ErrorCodes::InvalidLength, "record has to be <= 16.5MB"); } const int lenToAlloc = (doc->addPadding() && shouldPadInserts()) ? quantizeAllocationSpace(lenWHdr) : lenWHdr; StatusWith<DiskLoc> loc = allocRecord(txn, lenToAlloc, enforceQuota); if (!loc.isOK()) return StatusWith<RecordId>(loc.getStatus()); MmapV1RecordHeader* r = recordFor(loc.getValue()); fassert(17319, r->lengthWithHeaders() >= lenWHdr); r = reinterpret_cast<MmapV1RecordHeader*>(txn->recoveryUnit()->writingPtr(r, lenWHdr)); doc->writeDocument(r->data()); _addRecordToRecListInExtent(txn, r, loc.getValue()); _details->incrementStats(txn, r->netLength(), 1); return StatusWith<RecordId>(loc.getValue().toRecordId()); }
StatusWith<RecordId> RecordStoreV1Base::updateRecord(OperationContext* txn, const RecordId& oldLocation, const char* data, int dataSize, bool enforceQuota, UpdateNotifier* notifier) { MmapV1RecordHeader* oldRecord = recordFor(DiskLoc::fromRecordId(oldLocation)); if (oldRecord->netLength() >= dataSize) { // Make sure to notify other queries before we do an in-place update. if (notifier) { Status callbackStatus = notifier->recordStoreGoingToUpdateInPlace(txn, oldLocation); if (!callbackStatus.isOK()) return StatusWith<RecordId>(callbackStatus); } // we fit memcpy(txn->recoveryUnit()->writingPtr(oldRecord->data(), dataSize), data, dataSize); return StatusWith<RecordId>(oldLocation); } // We enforce the restriction of unchanging capped doc sizes above the storage layer. invariant(!isCapped()); // we have to move if (dataSize + MmapV1RecordHeader::HeaderSize > MaxAllowedAllocation) { return StatusWith<RecordId>(ErrorCodes::InvalidLength, "record has to be <= 16.5MB"); } StatusWith<RecordId> newLocation = _insertRecord(txn, data, dataSize, enforceQuota); if (!newLocation.isOK()) return newLocation; // insert worked, so we delete old record if (notifier) { Status moveStatus = notifier->recordStoreGoingToMove( txn, oldLocation, oldRecord->data(), oldRecord->netLength()); if (!moveStatus.isOK()) return StatusWith<RecordId>(moveStatus); } deleteRecord(txn, oldLocation); return newLocation; }
StatusWith<RecordData> RecordStoreV1Base::updateWithDamages( OperationContext* txn, const RecordId& loc, const RecordData& oldRec, const char* damageSource, const mutablebson::DamageVector& damages) { MmapV1RecordHeader* rec = recordFor(DiskLoc::fromRecordId(loc)); char* root = rec->data(); // All updates were in place. Apply them via durability and writing pointer. mutablebson::DamageVector::const_iterator where = damages.begin(); const mutablebson::DamageVector::const_iterator end = damages.end(); for (; where != end; ++where) { const char* sourcePtr = damageSource + where->sourceOffset; void* targetPtr = txn->recoveryUnit()->writingPtr(root + where->targetOffset, where->size); std::memcpy(targetPtr, sourcePtr, where->size); } return rec->toRecordData(); }
void RecordStoreV1Base::deleteRecord(OperationContext* txn, const RecordId& rid) { const DiskLoc dl = DiskLoc::fromRecordId(rid); MmapV1RecordHeader* todelete = recordFor(dl); invariant(todelete->netLength() >= 4); // this is required for defensive code /* remove ourself from the record next/prev chain */ { if (todelete->prevOfs() != DiskLoc::NullOfs) { DiskLoc prev = getPrevRecordInExtent(txn, dl); MmapV1RecordHeader* prevRecord = recordFor(prev); txn->recoveryUnit()->writingInt(prevRecord->nextOfs()) = todelete->nextOfs(); } if (todelete->nextOfs() != DiskLoc::NullOfs) { DiskLoc next = getNextRecord(txn, dl); MmapV1RecordHeader* nextRecord = recordFor(next); txn->recoveryUnit()->writingInt(nextRecord->prevOfs()) = todelete->prevOfs(); } } /* remove ourself from extent pointers */ { DiskLoc extentLoc = todelete->myExtentLoc(dl); Extent* e = _getExtent(txn, extentLoc); if (e->firstRecord == dl) { txn->recoveryUnit()->writing(&e->firstRecord); if (todelete->nextOfs() == DiskLoc::NullOfs) e->firstRecord.Null(); else e->firstRecord.set(dl.a(), todelete->nextOfs()); } if (e->lastRecord == dl) { txn->recoveryUnit()->writing(&e->lastRecord); if (todelete->prevOfs() == DiskLoc::NullOfs) e->lastRecord.Null(); else e->lastRecord.set(dl.a(), todelete->prevOfs()); } } /* add to the free list */ { _details->incrementStats(txn, -1 * todelete->netLength(), -1); if (_isSystemIndexes) { /* temp: if in system.indexes, don't reuse, and zero out: we want to be careful until validated more, as IndexDetails has pointers to this disk location. so an incorrectly done remove would cause a lot of problems. */ memset(txn->recoveryUnit()->writingPtr(todelete, todelete->lengthWithHeaders()), 0, todelete->lengthWithHeaders()); } else { // this is defensive so we can detect if we are still using a location // that was deleted memset(txn->recoveryUnit()->writingPtr(todelete->data(), 4), 0xee, 4); addDeletedRec(txn, dl); } } }