This is an automated email from the ASF dual-hosted git repository. kturner pushed a commit to branch elasticity in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/elasticity by this push: new 9c4c675e99 Moves split recovery code to upgrade (#4136) 9c4c675e99 is described below commit 9c4c675e99f97d8562737ee479139a81f89e2f57 Author: Keith Turner <ktur...@apache.org> AuthorDate: Thu Jan 11 18:50:34 2024 -0500 Moves split recovery code to upgrade (#4136) Splits write three mutations to the metadata table. In the case of process death all writes may not happen. Splits now run in FATE so this is not a concern, but did not previously run in FATE. There was custom code to handle this failure case that wrote specialized columns to the metadata table. Handling of these specialized columns was moved into the upgrade code. Existing test were adpated to test the moved code. One test was removed because it was no longer relavant. The test simulated split recovery on tablet load which is something that no longer happens, so the test was removed. --- .../core/metadata/schema/MetadataSchema.java | 30 ++- .../core/metadata/schema/TabletMetadata.java | 49 +--- .../server/constraints/MetadataConstraints.java | 5 +- .../accumulo/server/util/ManagerMetadataUtil.java | 171 ------------ .../accumulo/server/util/MetadataTableUtil.java | 117 -------- .../manager/upgrade/SplitRecovery12to13.java | 299 +++++++++++++++++++++ .../accumulo/manager/upgrade/Upgrader12to13.java | 19 ++ .../apache/accumulo/tserver/AssignmentHandler.java | 23 -- .../org/apache/accumulo/test/SplitRecoveryIT.java | 146 ---------- .../accumulo/test/functional/SplitRecoveryIT.java | 43 +-- 10 files changed, 370 insertions(+), 532 deletions(-) diff --git a/core/src/main/java/org/apache/accumulo/core/metadata/schema/MetadataSchema.java b/core/src/main/java/org/apache/accumulo/core/metadata/schema/MetadataSchema.java index 6a7b75a679..a69651e3d0 100644 --- a/core/src/main/java/org/apache/accumulo/core/metadata/schema/MetadataSchema.java +++ b/core/src/main/java/org/apache/accumulo/core/metadata/schema/MetadataSchema.java @@ -173,19 +173,6 @@ public class MetadataSchema { return per; } - /** - * A temporary field in case a split fails and we need to roll back - */ - public static final String OLD_PREV_ROW_QUAL = "oldprevrow"; - public static final ColumnFQ OLD_PREV_ROW_COLUMN = - new ColumnFQ(NAME, new Text(OLD_PREV_ROW_QUAL)); - /** - * A temporary field for splits to optimize certain operations - */ - public static final String SPLIT_RATIO_QUAL = "splitRatio"; - public static final ColumnFQ SPLIT_RATIO_COLUMN = - new ColumnFQ(NAME, new Text(SPLIT_RATIO_QUAL)); - /** * Creates a mutation that encodes a KeyExtent as a prevRow entry. */ @@ -433,6 +420,23 @@ public class MetadataSchema { public static final String REQUESTED_QUAL = "requested"; public static final ColumnFQ REQUESTED_COLUMN = new ColumnFQ(NAME, new Text(REQUESTED_QUAL)); } + + // These can be removed when the corresponding upgrade code is removed + public static class Upgrade12to13 { + + /** + * A temporary field in case a split fails and we need to roll back + */ + public static final String OLD_PREV_ROW_QUAL = "oldprevrow"; + public static final ColumnFQ OLD_PREV_ROW_COLUMN = + new ColumnFQ(TabletColumnFamily.NAME, new Text(OLD_PREV_ROW_QUAL)); + /** + * A temporary field for splits to optimize certain operations + */ + public static final String SPLIT_RATIO_QUAL = "splitRatio"; + public static final ColumnFQ SPLIT_RATIO_COLUMN = + new ColumnFQ(TabletColumnFamily.NAME, new Text(SPLIT_RATIO_QUAL)); + } } /** diff --git a/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java b/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java index df37c05566..c12760fb5d 100644 --- a/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java +++ b/core/src/main/java/org/apache/accumulo/core/metadata/schema/TabletMetadata.java @@ -25,9 +25,7 @@ import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSec import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.OPID_QUAL; import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.SELECTED_QUAL; import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.TIME_QUAL; -import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily.OLD_PREV_ROW_QUAL; import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_QUAL; -import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily.SPLIT_RATIO_QUAL; import java.util.Collection; import java.util.EnumSet; @@ -97,8 +95,6 @@ public class TabletMetadata { private TableId tableId; private Text prevEndRow; private boolean sawPrevEndRow = false; - private Text oldPrevEndRow; - private boolean sawOldPrevEndRow = false; private Text endRow; private Location location; private Map<StoredTabletFile,DataFileValue> files; @@ -115,7 +111,6 @@ public class TabletMetadata { private SortedMap<Key,Value> keyValues; private OptionalLong flush = OptionalLong.empty(); private List<LogEntry> logs; - private Double splitRatio = null; private Map<ExternalCompactionId,CompactionMetadata> extCompactions; private boolean merged; private TabletHostingGoal goal = TabletHostingGoal.ONDEMAND; @@ -135,7 +130,6 @@ public class TabletMetadata { public enum ColumnType { LOCATION, PREV_ROW, - OLD_PREV_ROW, FILES, LAST, LOADED, @@ -145,7 +139,6 @@ public class TabletMetadata { CLONED, FLUSH_ID, LOGS, - SPLIT_RATIO, SUSPEND, ECOMP, MERGED, @@ -280,21 +273,6 @@ public class TabletMetadata { return sawPrevEndRow; } - public Text getOldPrevEndRow() { - ensureFetched(ColumnType.OLD_PREV_ROW); - if (!sawOldPrevEndRow) { - throw new IllegalStateException( - "No old prev endrow seen. tableId: " + tableId + " endrow: " + endRow); - } - return oldPrevEndRow; - } - - // ELASTICITY_TODO remove and handle in upgrade - public boolean sawOldPrevEndRow() { - ensureFetched(ColumnType.OLD_PREV_ROW); - return sawOldPrevEndRow; - } - public Text getEndRow() { return endRow; } @@ -369,11 +347,6 @@ public class TabletMetadata { return flush; } - public Double getSplitRatio() { - ensureFetched(ColumnType.SPLIT_RATIO); - return splitRatio; - } - public boolean hasMerged() { ensureFetched(ColumnType.MERGED); return merged; @@ -397,14 +370,13 @@ public class TabletMetadata { public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("tableId", tableId) .append("prevEndRow", prevEndRow).append("sawPrevEndRow", sawPrevEndRow) - .append("oldPrevEndRow", oldPrevEndRow).append("sawOldPrevEndRow", sawOldPrevEndRow) .append("endRow", endRow).append("location", location).append("files", files) .append("scans", scans).append("loadedFiles", loadedFiles) .append("fetchedCols", fetchedCols).append("extent", extent).append("last", last) .append("suspend", suspend).append("dirName", dirName).append("time", time) .append("cloned", cloned).append("flush", flush).append("logs", logs) - .append("splitRatio", splitRatio).append("extCompactions", extCompactions) - .append("goal", goal).append("onDemandHostingRequested", onDemandHostingRequested) + .append("extCompactions", extCompactions).append("goal", goal) + .append("onDemandHostingRequested", onDemandHostingRequested) .append("operationId", operationId).append("selectedFiles", selectedFiles) .append("futureAndCurrentLocationSet", futureAndCurrentLocationSet).toString(); } @@ -477,18 +449,9 @@ public class TabletMetadata { switch (fam.toString()) { case TabletColumnFamily.STR_NAME: - switch (qual) { - case PREV_ROW_QUAL: - te.prevEndRow = TabletColumnFamily.decodePrevEndRow(kv.getValue()); - te.sawPrevEndRow = true; - break; - case OLD_PREV_ROW_QUAL: - te.oldPrevEndRow = TabletColumnFamily.decodePrevEndRow(kv.getValue()); - te.sawOldPrevEndRow = true; - break; - case SPLIT_RATIO_QUAL: - te.splitRatio = Double.parseDouble(val); - break; + if (qual.equals(PREV_ROW_QUAL)) { + te.prevEndRow = TabletColumnFamily.decodePrevEndRow(kv.getValue()); + te.sawPrevEndRow = true; } break; case ServerColumnFamily.STR_NAME: @@ -558,7 +521,7 @@ public class TabletMetadata { te.onDemandHostingRequested = true; break; default: - throw new IllegalStateException("Unexpected qualifier " + fam); + throw new IllegalStateException("Unexpected qualifier " + qual); } break; default: diff --git a/server/base/src/main/java/org/apache/accumulo/server/constraints/MetadataConstraints.java b/server/base/src/main/java/org/apache/accumulo/server/constraints/MetadataConstraints.java index dbf4daf4f5..6bf6efb64c 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/constraints/MetadataConstraints.java +++ b/server/base/src/main/java/org/apache/accumulo/server/constraints/MetadataConstraints.java @@ -55,6 +55,7 @@ import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Sc import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.SuspendLocationColumn; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily; +import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Upgrade12to13; import org.apache.accumulo.core.metadata.schema.SelectedFiles; import org.apache.accumulo.core.metadata.schema.TabletOperationId; import org.apache.accumulo.core.util.ColumnFQ; @@ -82,10 +83,10 @@ public class MetadataConstraints implements Constraint { // @formatter:off private static final Set<ColumnFQ> validColumnQuals = Set.of(TabletColumnFamily.PREV_ROW_COLUMN, - TabletColumnFamily.OLD_PREV_ROW_COLUMN, + Upgrade12to13.OLD_PREV_ROW_COLUMN, SuspendLocationColumn.SUSPEND_COLUMN, ServerColumnFamily.DIRECTORY_COLUMN, - TabletColumnFamily.SPLIT_RATIO_COLUMN, + Upgrade12to13.SPLIT_RATIO_COLUMN, ServerColumnFamily.TIME_COLUMN, ServerColumnFamily.LOCK_COLUMN, ServerColumnFamily.FLUSH_COLUMN, diff --git a/server/base/src/main/java/org/apache/accumulo/server/util/ManagerMetadataUtil.java b/server/base/src/main/java/org/apache/accumulo/server/util/ManagerMetadataUtil.java deleted file mode 100644 index 10ff4e9e55..0000000000 --- a/server/base/src/main/java/org/apache/accumulo/server/util/ManagerMetadataUtil.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.accumulo.server.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.SortedMap; -import java.util.TreeMap; - -import org.apache.accumulo.core.client.AccumuloException; -import org.apache.accumulo.core.client.Scanner; -import org.apache.accumulo.core.clientImpl.ScannerImpl; -import org.apache.accumulo.core.data.Key; -import org.apache.accumulo.core.data.PartialKey; -import org.apache.accumulo.core.data.Range; -import org.apache.accumulo.core.data.TableId; -import org.apache.accumulo.core.data.Value; -import org.apache.accumulo.core.dataImpl.KeyExtent; -import org.apache.accumulo.core.lock.ServiceLock; -import org.apache.accumulo.core.metadata.MetadataTable; -import org.apache.accumulo.core.metadata.ReferencedTabletFile; -import org.apache.accumulo.core.metadata.StoredTabletFile; -import org.apache.accumulo.core.metadata.TServerInstance; -import org.apache.accumulo.core.metadata.schema.Ample.TabletMutator; -import org.apache.accumulo.core.metadata.schema.DataFileValue; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataTime; -import org.apache.accumulo.core.metadata.schema.TabletMetadata; -import org.apache.accumulo.core.metadata.schema.TabletMetadata.Location; -import org.apache.accumulo.core.security.Authorizations; -import org.apache.accumulo.server.ServerContext; -import org.apache.hadoop.io.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ManagerMetadataUtil { - - private static final Logger log = LoggerFactory.getLogger(ManagerMetadataUtil.class); - - public static void addNewTablet(ServerContext context, KeyExtent extent, String dirName, - TServerInstance tServerInstance, Map<StoredTabletFile,DataFileValue> datafileSizes, - Map<Long,? extends Collection<ReferencedTabletFile>> bulkLoadedFiles, MetadataTime time, - long lastFlushID, ServiceLock zooLock) { - - // ELASTICITY_TODO intentionally not using conditional mutations for this code because its only - // called when tablets split. Tablet splitting will drastically change, so there is no need to - // update this to use conditional mutations ATM. - - TabletMutator tablet = context.getAmple().mutateTablet(extent); - tablet.putPrevEndRow(extent.prevEndRow()); - tablet.putZooLock(context.getZooKeeperRoot(), zooLock); - tablet.putDirName(dirName); - tablet.putTime(time); - - if (lastFlushID > 0) { - tablet.putFlushId(lastFlushID); - } - - if (tServerInstance != null) { - tablet.putLocation(Location.current(tServerInstance)); - tablet.deleteLocation(Location.future(tServerInstance)); - } - - datafileSizes.forEach((key, value) -> tablet.putFile(key, value)); - - for (Entry<Long,? extends Collection<ReferencedTabletFile>> entry : bulkLoadedFiles - .entrySet()) { - for (ReferencedTabletFile ref : entry.getValue()) { - tablet.putBulkFile(ref, entry.getKey()); - } - } - - tablet.mutate(); - } - - // ELASTICITY_TODO refactor this to be called in the upgrade code - public static KeyExtent fixSplit(ServerContext context, TabletMetadata meta, ServiceLock lock) - throws AccumuloException { - log.info("Incomplete split {} attempting to fix", meta.getExtent()); - - if (meta.getSplitRatio() == null) { - throw new IllegalArgumentException( - "Metadata entry does not have split ratio (" + meta.getExtent() + ")"); - } - - if (meta.getTime() == null) { - throw new IllegalArgumentException( - "Metadata entry does not have time (" + meta.getExtent() + ")"); - } - - return fixSplit(context, meta.getTableId(), meta.getExtent().toMetaRow(), meta.getPrevEndRow(), - meta.getOldPrevEndRow(), meta.getSplitRatio(), lock); - } - - private static KeyExtent fixSplit(ServerContext context, TableId tableId, Text metadataEntry, - Text metadataPrevEndRow, Text oper, double splitRatio, ServiceLock lock) - throws AccumuloException { - if (metadataPrevEndRow == null) { - // something is wrong, this should not happen... if a tablet is split, it will always have a - // prev end row.... - throw new AccumuloException( - "Split tablet does not have prev end row, something is amiss, extent = " + metadataEntry); - } - - // check to see if prev tablet exist in metadata tablet - Key prevRowKey = new Key(new Text(TabletsSection.encodeRow(tableId, metadataPrevEndRow))); - - try (ScannerImpl scanner2 = new ScannerImpl(context, MetadataTable.ID, Authorizations.EMPTY)) { - scanner2.setRange(new Range(prevRowKey, prevRowKey.followingKey(PartialKey.ROW))); - - if (scanner2.iterator().hasNext()) { - log.info("Finishing incomplete split {} {}", metadataEntry, metadataPrevEndRow); - - List<StoredTabletFile> highDatafilesToRemove = new ArrayList<>(); - - SortedMap<StoredTabletFile,DataFileValue> origDatafileSizes = new TreeMap<>(); - SortedMap<StoredTabletFile,DataFileValue> highDatafileSizes = new TreeMap<>(); - SortedMap<StoredTabletFile,DataFileValue> lowDatafileSizes = new TreeMap<>(); - - Key rowKey = new Key(metadataEntry); - try (Scanner scanner3 = new ScannerImpl(context, MetadataTable.ID, Authorizations.EMPTY)) { - - scanner3.fetchColumnFamily(DataFileColumnFamily.NAME); - scanner3.setRange(new Range(rowKey, rowKey.followingKey(PartialKey.ROW))); - - for (Entry<Key,Value> entry : scanner3) { - if (entry.getKey().compareColumnFamily(DataFileColumnFamily.NAME) == 0) { - StoredTabletFile stf = - new StoredTabletFile(entry.getKey().getColumnQualifierData().toString()); - origDatafileSizes.put(stf, new DataFileValue(entry.getValue().get())); - } - } - } - - MetadataTableUtil.splitDatafiles(metadataPrevEndRow, splitRatio, new HashMap<>(), - origDatafileSizes, lowDatafileSizes, highDatafileSizes, highDatafilesToRemove); - - MetadataTableUtil.finishSplit(metadataEntry, highDatafileSizes, highDatafilesToRemove, - context, lock); - - return KeyExtent.fromMetaRow(rowKey.getRow(), metadataPrevEndRow); - } else { - log.info("Rolling back incomplete split {} {}", metadataEntry, metadataPrevEndRow); - MetadataTableUtil.rollBackSplit(metadataEntry, oper, context, lock); - return KeyExtent.fromMetaRow(metadataEntry, oper); - } - } - } - -} diff --git a/server/base/src/main/java/org/apache/accumulo/server/util/MetadataTableUtil.java b/server/base/src/main/java/org/apache/accumulo/server/util/MetadataTableUtil.java index 4da05f2a5f..25406d9d59 100644 --- a/server/base/src/main/java/org/apache/accumulo/server/util/MetadataTableUtil.java +++ b/server/base/src/main/java/org/apache/accumulo/server/util/MetadataTableUtil.java @@ -51,8 +51,6 @@ import org.apache.accumulo.core.client.BatchWriterConfig; import org.apache.accumulo.core.client.MutationsRejectedException; import org.apache.accumulo.core.client.Scanner; import org.apache.accumulo.core.client.TableNotFoundException; -import org.apache.accumulo.core.client.admin.TabletHostingGoal; -import org.apache.accumulo.core.client.admin.TimeType; import org.apache.accumulo.core.clientImpl.BatchWriterImpl; import org.apache.accumulo.core.clientImpl.ScannerImpl; import org.apache.accumulo.core.data.Key; @@ -70,15 +68,12 @@ import org.apache.accumulo.core.metadata.StoredTabletFile; import org.apache.accumulo.core.metadata.schema.Ample; import org.apache.accumulo.core.metadata.schema.Ample.TabletMutator; import org.apache.accumulo.core.metadata.schema.DataFileValue; -import org.apache.accumulo.core.metadata.schema.ExternalCompactionId; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ClonedColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.CurrentLocationColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ExternalCompactionColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.LastLocationColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataTime; import org.apache.accumulo.core.metadata.schema.TabletDeletedException; import org.apache.accumulo.core.metadata.schema.TabletMetadata; @@ -158,67 +153,6 @@ public class MetadataTableUtil { return newFiles; } - public static void addTablet(KeyExtent extent, String path, ServerContext context, - TimeType timeType, ServiceLock zooLock, TabletHostingGoal goal) { - TabletMutator tablet = context.getAmple().mutateTablet(extent); - tablet.putPrevEndRow(extent.prevEndRow()); - tablet.putDirName(path); - tablet.putTime(new MetadataTime(0, timeType)); - tablet.putZooLock(context.getZooKeeperRoot(), zooLock); - tablet.putHostingGoal(goal); - tablet.mutate(); - - } - - public static void rollBackSplit(Text metadataEntry, Text oldPrevEndRow, ServerContext context, - ServiceLock zooLock) { - KeyExtent ke = KeyExtent.fromMetaRow(metadataEntry, oldPrevEndRow); - Mutation m = TabletColumnFamily.createPrevRowMutation(ke); - TabletColumnFamily.SPLIT_RATIO_COLUMN.putDelete(m); - TabletColumnFamily.OLD_PREV_ROW_COLUMN.putDelete(m); - update(context, zooLock, m, KeyExtent.fromMetaRow(metadataEntry)); - } - - public static void splitTablet(KeyExtent extent, Text oldPrevEndRow, double splitRatio, - ServerContext context, ServiceLock zooLock, Set<ExternalCompactionId> ecids) { - Mutation m = TabletColumnFamily.createPrevRowMutation(extent); - - TabletColumnFamily.SPLIT_RATIO_COLUMN.put(m, new Value(Double.toString(splitRatio))); - - TabletColumnFamily.OLD_PREV_ROW_COLUMN.put(m, - TabletColumnFamily.encodePrevEndRow(oldPrevEndRow)); - - ecids.forEach(ecid -> m.putDelete(ExternalCompactionColumnFamily.STR_NAME, ecid.canonical())); - - update(context, zooLock, m, extent); - } - - public static void finishSplit(Text metadataEntry, - Map<StoredTabletFile,DataFileValue> datafileSizes, - List<StoredTabletFile> highDatafilesToRemove, final ServerContext context, - ServiceLock zooLock) { - Mutation m = new Mutation(metadataEntry); - TabletColumnFamily.SPLIT_RATIO_COLUMN.putDelete(m); - TabletColumnFamily.OLD_PREV_ROW_COLUMN.putDelete(m); - - for (Entry<StoredTabletFile,DataFileValue> entry : datafileSizes.entrySet()) { - m.put(DataFileColumnFamily.NAME, entry.getKey().getMetadataText(), - new Value(entry.getValue().encode())); - } - - for (StoredTabletFile pathToRemove : highDatafilesToRemove) { - m.putDelete(DataFileColumnFamily.NAME, pathToRemove.getMetadataText()); - } - - update(context, zooLock, m, KeyExtent.fromMetaRow(metadataEntry)); - } - - public static void finishSplit(KeyExtent extent, - Map<StoredTabletFile,DataFileValue> datafileSizes, - List<StoredTabletFile> highDatafilesToRemove, ServerContext context, ServiceLock zooLock) { - finishSplit(extent.toMetaRow(), datafileSizes, highDatafilesToRemove, context, zooLock); - } - public static void removeScanFiles(KeyExtent extent, Set<StoredTabletFile> scanFiles, ServerContext context, ServiceLock zooLock) { TabletMutator tablet = context.getAmple().mutateTablet(extent); @@ -227,57 +161,6 @@ public class MetadataTableUtil { tablet.mutate(); } - public static void splitDatafiles(Text midRow, double splitRatio, - Map<StoredTabletFile,FileUtil.FileInfo> firstAndLastRows, - SortedMap<StoredTabletFile,DataFileValue> datafiles, - SortedMap<StoredTabletFile,DataFileValue> lowDatafileSizes, - SortedMap<StoredTabletFile,DataFileValue> highDatafileSizes, - List<StoredTabletFile> highDatafilesToRemove) { - - for (Entry<StoredTabletFile,DataFileValue> entry : datafiles.entrySet()) { - - Text firstRow = null; - Text lastRow = null; - - boolean rowsKnown = false; - - FileUtil.FileInfo mfi = firstAndLastRows.get(entry.getKey()); - - if (mfi != null) { - firstRow = mfi.getFirstRow(); - lastRow = mfi.getLastRow(); - rowsKnown = true; - } - - if (rowsKnown && firstRow.compareTo(midRow) > 0) { - // only in high - long highSize = entry.getValue().getSize(); - long highEntries = entry.getValue().getNumEntries(); - highDatafileSizes.put(entry.getKey(), - new DataFileValue(highSize, highEntries, entry.getValue().getTime())); - } else if (rowsKnown && lastRow.compareTo(midRow) <= 0) { - // only in low - long lowSize = entry.getValue().getSize(); - long lowEntries = entry.getValue().getNumEntries(); - lowDatafileSizes.put(entry.getKey(), - new DataFileValue(lowSize, lowEntries, entry.getValue().getTime())); - - highDatafilesToRemove.add(entry.getKey()); - } else { - long lowSize = (long) Math.floor((entry.getValue().getSize() * splitRatio)); - long lowEntries = (long) Math.floor((entry.getValue().getNumEntries() * splitRatio)); - lowDatafileSizes.put(entry.getKey(), - new DataFileValue(lowSize, lowEntries, entry.getValue().getTime())); - - long highSize = (long) Math.ceil((entry.getValue().getSize() * (1.0 - splitRatio))); - long highEntries = - (long) Math.ceil((entry.getValue().getNumEntries() * (1.0 - splitRatio))); - highDatafileSizes.put(entry.getKey(), - new DataFileValue(highSize, highEntries, entry.getValue().getTime())); - } - } - } - public static void deleteTable(TableId tableId, boolean insertDeletes, ServerContext context, ServiceLock lock) throws AccumuloException { try (Scanner ms = new ScannerImpl(context, MetadataTable.ID, Authorizations.EMPTY); diff --git a/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/SplitRecovery12to13.java b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/SplitRecovery12to13.java new file mode 100644 index 0000000000..f2dcab23cd --- /dev/null +++ b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/SplitRecovery12to13.java @@ -0,0 +1,299 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.accumulo.manager.upgrade; + +import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Upgrade12to13.OLD_PREV_ROW_COLUMN; +import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Upgrade12to13.SPLIT_RATIO_COLUMN; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.accumulo.core.client.AccumuloException; +import org.apache.accumulo.core.client.Scanner; +import org.apache.accumulo.core.client.admin.TabletHostingGoal; +import org.apache.accumulo.core.client.admin.TimeType; +import org.apache.accumulo.core.clientImpl.ScannerImpl; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Mutation; +import org.apache.accumulo.core.data.PartialKey; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.TableId; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.dataImpl.KeyExtent; +import org.apache.accumulo.core.lock.ServiceLock; +import org.apache.accumulo.core.metadata.ReferencedTabletFile; +import org.apache.accumulo.core.metadata.StoredTabletFile; +import org.apache.accumulo.core.metadata.TServerInstance; +import org.apache.accumulo.core.metadata.schema.Ample; +import org.apache.accumulo.core.metadata.schema.Ample.TabletMutator; +import org.apache.accumulo.core.metadata.schema.DataFileValue; +import org.apache.accumulo.core.metadata.schema.ExternalCompactionId; +import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection; +import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily; +import org.apache.accumulo.core.metadata.schema.MetadataTime; +import org.apache.accumulo.core.metadata.schema.TabletMetadata.Location; +import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.server.ServerContext; +import org.apache.accumulo.server.util.FileUtil; +import org.apache.accumulo.server.util.MetadataTableUtil; +import org.apache.hadoop.io.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SplitRecovery12to13 { + + private static final Logger log = LoggerFactory.getLogger(SplitRecovery12to13.class); + + public static void addNewTablet(ServerContext context, KeyExtent extent, String dirName, + TServerInstance tServerInstance, Map<StoredTabletFile,DataFileValue> datafileSizes, + Map<Long,? extends Collection<ReferencedTabletFile>> bulkLoadedFiles, MetadataTime time, + long lastFlushID) { + + TabletMutator tablet = context.getAmple().mutateTablet(extent); + tablet.putPrevEndRow(extent.prevEndRow()); + tablet.putDirName(dirName); + tablet.putTime(time); + + if (lastFlushID > 0) { + tablet.putFlushId(lastFlushID); + } + + if (tServerInstance != null) { + tablet.putLocation(Location.current(tServerInstance)); + tablet.deleteLocation(Location.future(tServerInstance)); + } + + datafileSizes.forEach((key, value) -> tablet.putFile(key, value)); + + for (Entry<Long,? extends Collection<ReferencedTabletFile>> entry : bulkLoadedFiles + .entrySet()) { + for (ReferencedTabletFile ref : entry.getValue()) { + tablet.putBulkFile(ref, entry.getKey()); + } + } + + tablet.mutate(); + } + + public static KeyExtent fixSplit(ServerContext context, Text metadataEntry) + throws AccumuloException { + var tableId = KeyExtent.fromMetaRow(metadataEntry).tableId(); + + try (var scanner = + new ScannerImpl(context, Ample.DataLevel.of(tableId).metaTableId(), Authorizations.EMPTY)) { + scanner.setRange(new Range(metadataEntry)); + + Text oldPrev = null; + Double persistedSplitRatio = null; + Text metadataPrevEndRow = null; + + boolean sawOldPrev = false; + + for (var entry : scanner) { + if (OLD_PREV_ROW_COLUMN.hasColumns(entry.getKey())) { + oldPrev = TabletsSection.TabletColumnFamily.decodePrevEndRow(entry.getValue()); + sawOldPrev = true; + } else if (SPLIT_RATIO_COLUMN.hasColumns(entry.getKey())) { + persistedSplitRatio = Double.parseDouble(entry.getValue().toString()); + } else if (TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.hasColumns(entry.getKey())) { + metadataPrevEndRow = TabletsSection.TabletColumnFamily.decodePrevEndRow(entry.getValue()); + } + } + + if (!sawOldPrev || persistedSplitRatio == null || metadataPrevEndRow == null) { + throw new IllegalStateException("Missing expected columns " + metadataEntry); + } + + return fixSplit(context, tableId, metadataEntry, metadataPrevEndRow, oldPrev, + persistedSplitRatio); + } + } + + private static KeyExtent fixSplit(ServerContext context, TableId tableId, Text metadataEntry, + Text metadataPrevEndRow, Text oper, double splitRatio) throws AccumuloException { + if (metadataPrevEndRow == null) { + // something is wrong, this should not happen... if a tablet is split, it will always have a + // prev end row.... + throw new AccumuloException( + "Split tablet does not have prev end row, something is amiss, extent = " + metadataEntry); + } + + // check to see if prev tablet exist in metadata tablet + Key prevRowKey = new Key(new Text(TabletsSection.encodeRow(tableId, metadataPrevEndRow))); + + try (Scanner scanner2 = + new ScannerImpl(context, Ample.DataLevel.of(tableId).metaTableId(), Authorizations.EMPTY)) { + scanner2.setRange(new Range(prevRowKey, prevRowKey.followingKey(PartialKey.ROW))); + + if (scanner2.iterator().hasNext()) { + log.info("Finishing incomplete split {} {}", metadataEntry, metadataPrevEndRow); + + List<StoredTabletFile> highDatafilesToRemove = new ArrayList<>(); + + SortedMap<StoredTabletFile,DataFileValue> origDatafileSizes = new TreeMap<>(); + SortedMap<StoredTabletFile,DataFileValue> highDatafileSizes = new TreeMap<>(); + SortedMap<StoredTabletFile,DataFileValue> lowDatafileSizes = new TreeMap<>(); + + Key rowKey = new Key(metadataEntry); + try (Scanner scanner3 = new ScannerImpl(context, Ample.DataLevel.of(tableId).metaTableId(), + Authorizations.EMPTY)) { + + scanner3.fetchColumnFamily(DataFileColumnFamily.NAME); + scanner3.setRange(new Range(rowKey, rowKey.followingKey(PartialKey.ROW))); + + for (Entry<Key,Value> entry : scanner3) { + if (entry.getKey().compareColumnFamily(DataFileColumnFamily.NAME) == 0) { + StoredTabletFile stf = + new StoredTabletFile(entry.getKey().getColumnQualifierData().toString()); + origDatafileSizes.put(stf, new DataFileValue(entry.getValue().get())); + } + } + } + + splitDatafiles(metadataPrevEndRow, splitRatio, new HashMap<>(), origDatafileSizes, + lowDatafileSizes, highDatafileSizes, highDatafilesToRemove); + + finishSplit(metadataEntry, highDatafileSizes, highDatafilesToRemove, context); + + return KeyExtent.fromMetaRow(rowKey.getRow(), metadataPrevEndRow); + } else { + log.info("Rolling back incomplete split {} {}", metadataEntry, metadataPrevEndRow); + rollBackSplit(metadataEntry, oper, context); + return KeyExtent.fromMetaRow(metadataEntry, oper); + } + } + } + + public static void splitDatafiles(Text midRow, double splitRatio, + Map<StoredTabletFile,FileUtil.FileInfo> firstAndLastRows, + SortedMap<StoredTabletFile,DataFileValue> datafiles, + SortedMap<StoredTabletFile,DataFileValue> lowDatafileSizes, + SortedMap<StoredTabletFile,DataFileValue> highDatafileSizes, + List<StoredTabletFile> highDatafilesToRemove) { + + for (Entry<StoredTabletFile,DataFileValue> entry : datafiles.entrySet()) { + + Text firstRow = null; + Text lastRow = null; + + boolean rowsKnown = false; + + FileUtil.FileInfo mfi = firstAndLastRows.get(entry.getKey()); + + if (mfi != null) { + firstRow = mfi.getFirstRow(); + lastRow = mfi.getLastRow(); + rowsKnown = true; + } + + if (rowsKnown && firstRow.compareTo(midRow) > 0) { + // only in high + long highSize = entry.getValue().getSize(); + long highEntries = entry.getValue().getNumEntries(); + highDatafileSizes.put(entry.getKey(), + new DataFileValue(highSize, highEntries, entry.getValue().getTime())); + } else if (rowsKnown && lastRow.compareTo(midRow) <= 0) { + // only in low + long lowSize = entry.getValue().getSize(); + long lowEntries = entry.getValue().getNumEntries(); + lowDatafileSizes.put(entry.getKey(), + new DataFileValue(lowSize, lowEntries, entry.getValue().getTime())); + + highDatafilesToRemove.add(entry.getKey()); + } else { + long lowSize = (long) Math.floor((entry.getValue().getSize() * splitRatio)); + long lowEntries = (long) Math.floor((entry.getValue().getNumEntries() * splitRatio)); + lowDatafileSizes.put(entry.getKey(), + new DataFileValue(lowSize, lowEntries, entry.getValue().getTime())); + + long highSize = (long) Math.ceil((entry.getValue().getSize() * (1.0 - splitRatio))); + long highEntries = + (long) Math.ceil((entry.getValue().getNumEntries() * (1.0 - splitRatio))); + highDatafileSizes.put(entry.getKey(), + new DataFileValue(highSize, highEntries, entry.getValue().getTime())); + } + } + } + + public static void rollBackSplit(Text metadataEntry, Text oldPrevEndRow, ServerContext context) { + KeyExtent ke = KeyExtent.fromMetaRow(metadataEntry, oldPrevEndRow); + Mutation m = TabletsSection.TabletColumnFamily.createPrevRowMutation(ke); + SPLIT_RATIO_COLUMN.putDelete(m); + OLD_PREV_ROW_COLUMN.putDelete(m); + MetadataTableUtil.update(context, null, m, KeyExtent.fromMetaRow(metadataEntry)); + } + + public static void splitTablet(KeyExtent extent, Text oldPrevEndRow, double splitRatio, + ServerContext context, Set<ExternalCompactionId> ecids) { + Mutation m = TabletsSection.TabletColumnFamily.createPrevRowMutation(extent); + + SPLIT_RATIO_COLUMN.put(m, new Value(Double.toString(splitRatio))); + + OLD_PREV_ROW_COLUMN.put(m, TabletsSection.TabletColumnFamily.encodePrevEndRow(oldPrevEndRow)); + + ecids.forEach(ecid -> m.putDelete(TabletsSection.ExternalCompactionColumnFamily.STR_NAME, + ecid.canonical())); + + MetadataTableUtil.update(context, null, m, extent); + } + + public static void finishSplit(Text metadataEntry, + Map<StoredTabletFile,DataFileValue> datafileSizes, + List<StoredTabletFile> highDatafilesToRemove, final ServerContext context) { + Mutation m = new Mutation(metadataEntry); + SPLIT_RATIO_COLUMN.putDelete(m); + OLD_PREV_ROW_COLUMN.putDelete(m); + + for (Entry<StoredTabletFile,DataFileValue> entry : datafileSizes.entrySet()) { + m.put(DataFileColumnFamily.NAME, entry.getKey().getMetadataText(), + new Value(entry.getValue().encode())); + } + + for (StoredTabletFile pathToRemove : highDatafilesToRemove) { + m.putDelete(DataFileColumnFamily.NAME, pathToRemove.getMetadataText()); + } + + MetadataTableUtil.update(context, null, m, KeyExtent.fromMetaRow(metadataEntry)); + } + + public static void finishSplit(KeyExtent extent, + Map<StoredTabletFile,DataFileValue> datafileSizes, + List<StoredTabletFile> highDatafilesToRemove, ServerContext context) { + finishSplit(extent.toMetaRow(), datafileSizes, highDatafilesToRemove, context); + } + + public static void addTablet(KeyExtent extent, String path, ServerContext context, + TimeType timeType, ServiceLock zooLock, TabletHostingGoal goal) { + TabletMutator tablet = context.getAmple().mutateTablet(extent); + tablet.putPrevEndRow(extent.prevEndRow()); + tablet.putDirName(path); + tablet.putTime(new MetadataTime(0, timeType)); + tablet.putZooLock(context.getZooKeeperRoot(), zooLock); + tablet.putHostingGoal(goal); + tablet.mutate(); + + } +} diff --git a/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader12to13.java b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader12to13.java index 9c657d27d1..e5626bec9a 100644 --- a/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader12to13.java +++ b/server/manager/src/main/java/org/apache/accumulo/manager/upgrade/Upgrader12to13.java @@ -46,6 +46,7 @@ import org.apache.accumulo.core.metadata.schema.RootTabletMetadata; import org.apache.accumulo.core.metadata.schema.TabletMetadata.ColumnType; import org.apache.accumulo.core.metadata.schema.TabletsMetadata; import org.apache.accumulo.core.schema.Section; +import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.core.util.ColumnFQ; import org.apache.accumulo.server.ServerContext; import org.apache.accumulo.server.conf.store.TablePropKey; @@ -77,6 +78,8 @@ public class Upgrader12to13 implements Upgrader { @Override public void upgradeRoot(ServerContext context) { + LOG.info("Looking for partial splits"); + handlePartialSplits(context, RootTable.NAME); LOG.info("Setting metadata table hosting goal"); addHostingGoalToMetadataTable(context); LOG.info("Removing MetadataBulkLoadFilter iterator from root table"); @@ -87,6 +90,8 @@ public class Upgrader12to13 implements Upgrader { @Override public void upgradeMetadata(ServerContext context) { + LOG.info("Looking for partial splits"); + handlePartialSplits(context, MetadataTable.NAME); LOG.info("Setting hosting goal on user tables"); addHostingGoalToUserTables(context); LOG.info("Deleting external compaction final states from user tables"); @@ -120,6 +125,7 @@ public class Upgrader12to13 implements Upgrader { Preconditions.checkState(key.getColumnVisibilityData().length() == 0, "Expected empty visibility, saw %s ", key.getColumnVisibilityData()); Mutation m = new Mutation(row); + // TODO will metadata contraint fail when this is written? COMPACT_COL.putDelete(m); mutations.add(m); } @@ -269,4 +275,17 @@ public class Upgrader12to13 implements Upgrader { throw new IllegalStateException(e); } } + + private void handlePartialSplits(ServerContext context, String table) { + try (var scanner = context.createScanner(table, Authorizations.EMPTY)) { + scanner.setRange(TabletsSection.getRange()); + TabletsSection.Upgrade12to13.SPLIT_RATIO_COLUMN.fetch(scanner); + + for (var entry : scanner) { + SplitRecovery12to13.fixSplit(context, entry.getKey().getRow()); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } + } } diff --git a/server/tserver/src/main/java/org/apache/accumulo/tserver/AssignmentHandler.java b/server/tserver/src/main/java/org/apache/accumulo/tserver/AssignmentHandler.java index 0b4b2f21ff..cef355cbf1 100644 --- a/server/tserver/src/main/java/org/apache/accumulo/tserver/AssignmentHandler.java +++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/AssignmentHandler.java @@ -21,9 +21,7 @@ package org.apache.accumulo.tserver; import static java.util.concurrent.TimeUnit.MINUTES; import static org.apache.accumulo.server.problems.ProblemType.TABLET_LOAD; -import java.util.Arrays; import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.TimeUnit; import org.apache.accumulo.core.client.AccumuloException; @@ -109,27 +107,6 @@ class AssignmentHandler implements Runnable { tabletMetadata = server.getContext().getAmple().readTablet(extent); canLoad = checkTabletMetadata(extent, server.getTabletSession(), tabletMetadata); - - if (canLoad && tabletMetadata.sawOldPrevEndRow()) { - KeyExtent fixedExtent = tabletMetadata.getExtent(); - - synchronized (server.openingTablets) { - server.openingTablets.remove(extent); - server.openingTablets.notifyAll(); - // it expected that the new extent will overlap the old one... if it does not, it - // should not be added to unopenedTablets - if (!KeyExtent.findOverlapping(extent, new TreeSet<>(Arrays.asList(fixedExtent))) - .contains(fixedExtent)) { - throw new IllegalStateException( - "Fixed split does not overlap " + extent + " " + fixedExtent); - } - server.unopenedTablets.add(fixedExtent); - } - // split was rolled back... try again - new AssignmentHandler(server, fixedExtent).run(); - return; - - } } catch (Exception e) { synchronized (server.openingTablets) { server.openingTablets.remove(extent); diff --git a/test/src/main/java/org/apache/accumulo/test/SplitRecoveryIT.java b/test/src/main/java/org/apache/accumulo/test/SplitRecoveryIT.java deleted file mode 100644 index f3acfa2b53..0000000000 --- a/test/src/main/java/org/apache/accumulo/test/SplitRecoveryIT.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.accumulo.test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.time.Duration; -import java.util.Map.Entry; - -import org.apache.accumulo.core.client.Accumulo; -import org.apache.accumulo.core.client.AccumuloClient; -import org.apache.accumulo.core.client.BatchWriter; -import org.apache.accumulo.core.client.Scanner; -import org.apache.accumulo.core.client.TableNotFoundException; -import org.apache.accumulo.core.data.Key; -import org.apache.accumulo.core.data.Mutation; -import org.apache.accumulo.core.data.Range; -import org.apache.accumulo.core.data.TableId; -import org.apache.accumulo.core.data.Value; -import org.apache.accumulo.core.dataImpl.KeyExtent; -import org.apache.accumulo.core.metadata.MetadataTable; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.CurrentLocationColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily; -import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily; -import org.apache.accumulo.core.security.Authorizations; -import org.apache.accumulo.core.security.TablePermission; -import org.apache.accumulo.harness.AccumuloClusterHarness; -import org.apache.hadoop.io.Text; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -@Disabled // ELASTICITY_TODO -public class SplitRecoveryIT extends AccumuloClusterHarness { - - // ELASTICITY_TODO: Confirm still works as intended - - @Override - protected Duration defaultTimeout() { - return Duration.ofMinutes(1); - } - - private Mutation m(String row) { - Mutation result = new Mutation(row); - result.put("cf", "cq", new Value("value")); - return result; - } - - boolean isOffline(String tablename, AccumuloClient client) throws TableNotFoundException { - String tableId = client.tableOperations().tableIdMap().get(tablename); - try (Scanner scanner = client.createScanner(MetadataTable.NAME, Authorizations.EMPTY)) { - scanner.setRange(new Range(new Text(tableId + ";"), new Text(tableId + "<"))); - scanner.fetchColumnFamily(CurrentLocationColumnFamily.NAME); - return scanner.stream().findAny().isEmpty(); - } - } - - @Test - public void test() throws Exception { - - String tableName = getUniqueNames(1)[0]; - - try (AccumuloClient client = Accumulo.newClient().from(getClientProps()).build()) { - for (int tn = 0; tn < 2; tn++) { - // create a table and put some data in it - client.tableOperations().create(tableName); - try (BatchWriter bw = client.createBatchWriter(tableName)) { - bw.addMutation(m("a")); - bw.addMutation(m("b")); - bw.addMutation(m("c")); - } - // take the table offline - client.tableOperations().offline(tableName); - while (!isOffline(tableName, client)) { - Thread.sleep(200); - } - - // poke a partial split into the metadata table - client.securityOperations().grantTablePermission(getAdminPrincipal(), MetadataTable.NAME, - TablePermission.WRITE); - TableId tableId = TableId.of(client.tableOperations().tableIdMap().get(tableName)); - - KeyExtent extent = new KeyExtent(tableId, null, new Text("b")); - Mutation m = TabletColumnFamily.createPrevRowMutation(extent); - - TabletColumnFamily.SPLIT_RATIO_COLUMN.put(m, new Value(Double.toString(0.5))); - TabletColumnFamily.OLD_PREV_ROW_COLUMN.put(m, TabletColumnFamily.encodePrevEndRow(null)); - try (BatchWriter bw = client.createBatchWriter(MetadataTable.NAME)) { - bw.addMutation(m); - - if (tn == 1) { - bw.flush(); - - try (Scanner scanner = client.createScanner(MetadataTable.NAME)) { - scanner.setRange(extent.toMetaRange()); - scanner.fetchColumnFamily(DataFileColumnFamily.NAME); - - KeyExtent extent2 = new KeyExtent(tableId, new Text("b"), null); - m = TabletColumnFamily.createPrevRowMutation(extent2); - ServerColumnFamily.DIRECTORY_COLUMN.put(m, new Value("t2")); - ServerColumnFamily.TIME_COLUMN.put(m, new Value("M0")); - - for (Entry<Key,Value> entry : scanner) { - m.put(DataFileColumnFamily.NAME, entry.getKey().getColumnQualifier(), - entry.getValue()); - } - bw.addMutation(m); - } - } - } - - // bring the table online - client.tableOperations().online(tableName); - - // verify the tablets went online - try (Scanner scanner = client.createScanner(tableName)) { - int i = 0; - String[] expected = {"a", "b", "c"}; - for (Entry<Key,Value> entry : scanner) { - assertEquals(expected[i], entry.getKey().getRow().toString()); - i++; - } - assertEquals(3, i); - - client.tableOperations().delete(tableName); - } - } - } - } -} diff --git a/test/src/main/java/org/apache/accumulo/test/functional/SplitRecoveryIT.java b/test/src/main/java/org/apache/accumulo/test/functional/SplitRecoveryIT.java index b457f93a0c..a196f0254e 100644 --- a/test/src/main/java/org/apache/accumulo/test/functional/SplitRecoveryIT.java +++ b/test/src/main/java/org/apache/accumulo/test/functional/SplitRecoveryIT.java @@ -18,6 +18,7 @@ */ package org.apache.accumulo.test.functional; +import static org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Upgrade12to13.SPLIT_RATIO_COLUMN; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -63,30 +64,26 @@ import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.Bu import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.CurrentLocationColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.DataFileColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.FutureLocationColumnFamily; +import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.HostingColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.LastLocationColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily; import org.apache.accumulo.core.metadata.schema.MetadataTime; -import org.apache.accumulo.core.metadata.schema.TabletMetadata; import org.apache.accumulo.core.metadata.schema.TabletMetadata.Location; import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.core.util.ColumnFQ; +import org.apache.accumulo.manager.upgrade.SplitRecovery12to13; import org.apache.accumulo.server.ServerContext; import org.apache.accumulo.server.manager.state.Assignment; -import org.apache.accumulo.server.util.ManagerMetadataUtil; import org.apache.accumulo.server.util.MetadataTableUtil; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -@Disabled // ELASTICITY_TODO public class SplitRecoveryIT extends ConfigurableMacBase { - // ELASTICITY_TODO: This functionality needs to be adpated to work on upgrade - @Override protected Duration defaultTimeout() { return Duration.ofMinutes(1); @@ -167,7 +164,7 @@ public class SplitRecoveryIT extends ConfigurableMacBase { String dirName = "dir_" + i; String tdir = context.getTablesDirs().iterator().next() + "/" + extent.tableId() + "/" + dirName; - MetadataTableUtil.addTablet(extent, dirName, context, TimeType.LOGICAL, zl, + SplitRecovery12to13.addTablet(extent, dirName, context, TimeType.LOGICAL, zl, TabletHostingGoal.ONDEMAND); SortedMap<ReferencedTabletFile,DataFileValue> dataFiles = new TreeMap<>(); dataFiles.put(new ReferencedTabletFile(new Path(tdir + "/" + RFile.EXTENSION + "_000_000")), @@ -209,10 +206,10 @@ public class SplitRecoveryIT extends ConfigurableMacBase { SortedMap<StoredTabletFile,DataFileValue> highDatafileSizes = new TreeMap<>(); List<StoredTabletFile> highDatafilesToRemove = new ArrayList<>(); - MetadataTableUtil.splitDatafiles(midRow, splitRatio, new HashMap<>(), dataFiles, + SplitRecovery12to13.splitDatafiles(midRow, splitRatio, new HashMap<>(), dataFiles, lowDatafileSizes, highDatafileSizes, highDatafilesToRemove); - MetadataTableUtil.splitTablet(high, extent.prevEndRow(), splitRatio, context, zl, Set.of()); + SplitRecovery12to13.splitTablet(high, extent.prevEndRow(), splitRatio, context, Set.of()); TServerInstance instance = new TServerInstance(location, zl.getSessionId()); Assignment assignment = new Assignment(high, instance, null); @@ -223,20 +220,29 @@ public class SplitRecoveryIT extends ConfigurableMacBase { if (steps >= 1) { Map<Long,List<ReferencedTabletFile>> bulkFiles = getBulkFilesLoaded(context, high); - ManagerMetadataUtil.addNewTablet(context, low, "lowDir", instance, lowDatafileSizes, - bulkFiles, new MetadataTime(0, TimeType.LOGICAL), -1L, zl); + SplitRecovery12to13.addNewTablet(context, low, "lowDir", instance, lowDatafileSizes, + bulkFiles, new MetadataTime(0, TimeType.LOGICAL), -1L); } if (steps >= 2) { - MetadataTableUtil.finishSplit(high, highDatafileSizes, highDatafilesToRemove, context, zl); + SplitRecovery12to13.finishSplit(high, highDatafileSizes, highDatafilesToRemove, context); } - TabletMetadata meta = context.getAmple().readTablet(high); - KeyExtent fixedExtent = ManagerMetadataUtil.fixSplit(context, meta, zl); - if (steps < 2) { - assertEquals(splitRatio, meta.getSplitRatio(), 0.0); + Double persistedSplitRatio = null; + + try (var scanner = context.createScanner(MetadataTable.NAME, Authorizations.EMPTY)) { + scanner.setRange(high.toMetaRange()); + for (var entry : scanner) { + if (SPLIT_RATIO_COLUMN.hasColumns(entry.getKey())) { + persistedSplitRatio = Double.parseDouble(entry.getValue().toString()); + } + } + } + assertEquals(splitRatio, persistedSplitRatio, 0.0); } + KeyExtent fixedExtent = SplitRecovery12to13.fixSplit(context, high.toMetaRow()); + if (steps >= 1) { assertEquals(high, fixedExtent); ensureTabletHasNoUnexpectedMetadataEntries(context, low, lowDatafileSizes); @@ -277,6 +283,7 @@ public class SplitRecoveryIT extends ConfigurableMacBase { expectedColumnFamilies.add(CurrentLocationColumnFamily.NAME); expectedColumnFamilies.add(LastLocationColumnFamily.NAME); expectedColumnFamilies.add(BulkFileColumnFamily.NAME); + expectedColumnFamilies.add(HostingColumnFamily.NAME); Iterator<Entry<Key,Value>> iter = scanner.iterator(); @@ -310,6 +317,9 @@ public class SplitRecoveryIT extends ConfigurableMacBase { "Tablet " + extent + " contained unexpected " + MetadataTable.NAME + " entry " + key); } + // This is not always present + expectedColumns.remove(ServerColumnFamily.LOCK_COLUMN); + if (expectedColumns.size() > 1 || (expectedColumns.size() == 1)) { throw new Exception("Not all expected columns seen " + extent + " " + expectedColumns); } @@ -349,5 +359,4 @@ public class SplitRecoveryIT extends ConfigurableMacBase { public void test() throws Exception { assertEquals(0, exec(SplitRecoveryIT.class).waitFor()); } - }