/** * Atomic update. * * @return <code>null</code> */ @Override protected Void doTask() throws Exception { updateEvent.start(); try { if (resourceManager.isOverflowAllowed()) throw new IllegalStateException(); final SegmentMetadata segmentMetadata = buildResult.segmentMetadata; if (INFO) log.info("Begin: name=" + getOnlyResource() + ", newSegment=" + segmentMetadata); /* * Open the unisolated B+Tree on the live journal that is * absorbing writes. We are going to update its index metadata. * * Note: I am using AbstractTask#getIndex(String name) so that * the concurrency control logic will notice the changes to the * BTree and cause it to be checkpointed if this task succeeds * normally. */ final ILocalBTreeView view = (ILocalBTreeView) getIndex(getOnlyResource()); // make sure that this is the same scale-out index. assertSameIndex(indexUUID, view.getMutableBTree()); if (view instanceof BTree) { /* * Note: there is an expectation that this is not a simple * BTree because this the build task is supposed to be * invoked after an overflow event, and that event should * have re-defined the view to include the BTree on the new * journal plus the historical view. * * One explanation for finding a simple view here is that * the view was a simple BTree on the old journal and the * data was copied from the old journal into the new journal * and then someone decided to do a build even through a * copy had already been done. However, this is not a very * good explanation since we try to avoid doing a build if * we have already done a copy! */ throw new RuntimeException( "View is only a B+Tree: name=" + buildResult.name + ", pmd=" + view.getIndexMetadata().getPartitionMetadata()); } // The live B+Tree. final BTree btree = view.getMutableBTree(); if (INFO) log.info( "src=" + getOnlyResource() + ",counter=" + view.getCounter().get() + ",checkpoint=" + btree.getCheckpoint()); assert btree != null : "Expecting index: " + getOnlyResource(); // clone the current metadata record for the live index. final IndexMetadata indexMetadata = btree.getIndexMetadata().clone(); /* * This is the index partition definition on the live index - * the one that will be replaced with a new view as the result * of this atomic update. */ final LocalPartitionMetadata currentpmd = indexMetadata.getPartitionMetadata(); // Check pre-conditions. final IResourceMetadata[] currentResources = currentpmd.getResources(); { if (currentpmd == null) { throw new IllegalStateException("Not an index partition: " + getOnlyResource()); } if (!currentResources[0].getUUID().equals(getJournal().getRootBlockView().getUUID())) { throw new IllegalStateException( "Expecting live journal to be the first resource: " + currentResources); } /* * Note: I have commented out a bunch of pre-condition tests * that are not valid for histories such as: * * history=create() register(0) split(0) * copy(entryCount=314) * * This case arises when there are not enough index entries * written on the journal after a split to warrant a build * so the buffered writes are just copied to the new * journal. The resources in the view are: * * 1. journal 2. segment * * And this update will replace the segment. */ // // the old journal's resource metadata. // final IResourceMetadata oldJournalMetadata = // oldResources[1]; // assert oldJournalMetadata != null; // assert oldJournalMetadata instanceof JournalMetadata : // "name=" // + getOnlyResource() + ", old pmd=" + oldpmd // + ", segmentMetadata=" + buildResult.segmentMetadata; // // // live journal must be newer. // assert journal.getRootBlockView().getCreateTime() > // oldJournalMetadata // .getCreateTime(); // new index segment build from a view that did not include // data from the live journal. assert segmentMetadata.getCreateTime() < getJournal().getRootBlockView().getFirstCommitTime() : "segment createTime LT journal 1st commit time" + ": segmentMetadata=" + segmentMetadata + ", journal: " + getJournal().getRootBlockView(); // if (oldResources.length == 3) { // // // the old index segment's resource metadata. // final IResourceMetadata oldSegmentMetadata = // oldResources[2]; // assert oldSegmentMetadata != null; // assert oldSegmentMetadata instanceof SegmentMetadata; // // assert oldSegmentMetadata.getCreateTime() <= // oldJournalMetadata // .getCreateTime(); // // } } // new view definition. final IResourceMetadata[] newResources = new IResourceMetadata[] { // the live journal. getJournal().getResourceMetadata(), // the newly built index segment. segmentMetadata }; // describe the index partition. indexMetadata.setPartitionMetadata( new LocalPartitionMetadata( // currentpmd.getPartitionId(), // currentpmd.getSourcePartitionId(), // currentpmd.getLeftSeparatorKey(), // currentpmd.getRightSeparatorKey(), // newResources, // currentpmd.getIndexPartitionCause() // currentpmd.getHistory() // + OverflowActionEnum.Merge// // + "(lastCommitTime=" // + segmentMetadata.getCreateTime()// // + ",btreeEntryCount=" // + btree.getEntryCount()// // + ",segmentEntryCount=" // + buildResult.builder.getCheckpoint().nentries// // + ",segment=" // + segmentMetadata.getUUID()// // + ",counter=" // + btree.getCounter().get()// // + ",oldResources=" // + Arrays.toString(currentResources) + ") " )); // update the metadata associated with the btree btree.setIndexMetadata(indexMetadata); if (INFO) log.info( "Updated view: name=" + getOnlyResource() + ", pmd=" + indexMetadata.getPartitionMetadata()); /* * Verify that the btree recognizes that it needs to be * checkpointed. * * Note: The atomic commit point is when this task commits. */ assert btree.needsCheckpoint(); // btree.writeCheckpoint(); // { // final long id0 = btree.getCounter().get(); // final long pid = id0 >> 32; // final long mask = 0xffffffffL; // final int ctr = (int) (id0 & mask); // log.warn("name="+getOnlyResource()+", counter="+id0+", pid="+pid+", // ctr="+ctr); // } // notify successful index partition build. resourceManager.overflowCounters.indexPartitionMergeCounter.incrementAndGet(); return null; } finally { updateEvent.end(); } } // doTask()