ACCUMULO-3301 Fetch future tablet column from metadata. We need to fetch both the future and current location for the tablets in the table to be sure that the table has correctly transitioned to the expected state. Without this, offline/online may incorrectly return before the table has actually fully transitioned to its final state.
Lifted the code to create the scanner to its own method to easily test it. Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/1f674b4e Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/1f674b4e Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/1f674b4e Branch: refs/heads/master Commit: 1f674b4effd84f2e30f52fd624d963a78999d0d4 Parents: 73068c0 Author: Josh Elser <els...@apache.org> Authored: Wed Nov 5 13:12:19 2014 -0500 Committer: Josh Elser <els...@apache.org> Committed: Wed Nov 5 15:27:21 2014 -0500 ---------------------------------------------------------------------- .../core/client/impl/TableOperationsImpl.java | 21 +++-- .../client/impl/TableOperationsImplTest.java | 86 ++++++++++++++++++++ 2 files changed, 102 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/accumulo/blob/1f674b4e/core/src/main/java/org/apache/accumulo/core/client/impl/TableOperationsImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/accumulo/core/client/impl/TableOperationsImpl.java b/core/src/main/java/org/apache/accumulo/core/client/impl/TableOperationsImpl.java index 547ccc7..07df1bd 100644 --- a/core/src/main/java/org/apache/accumulo/core/client/impl/TableOperationsImpl.java +++ b/core/src/main/java/org/apache/accumulo/core/client/impl/TableOperationsImpl.java @@ -1216,11 +1216,8 @@ public class TableOperationsImpl extends TableOperationsHelper { String metaTable = MetadataTable.NAME; if (tableId.equals(MetadataTable.ID)) metaTable = RootTable.NAME; - Scanner scanner = instance.getConnector(credentials.getPrincipal(), credentials.getToken()).createScanner(metaTable, Authorizations.EMPTY); - scanner = new IsolatedScanner(scanner); - TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.fetch(scanner); - scanner.fetchColumnFamily(TabletsSection.CurrentLocationColumnFamily.NAME); - scanner.setRange(range); + + Scanner scanner = createMetadataScanner(instance, credentials, metaTable, range); RowIterator rowIter = new RowIterator(scanner); @@ -1307,6 +1304,20 @@ public class TableOperationsImpl extends TableOperationsHelper { } } + /** + * Create an IsolatedScanner over the given table, fetching the columns necessary to determine when a table has transitioned to online or offline. + */ + protected IsolatedScanner createMetadataScanner(Instance inst, Credentials creds, String metaTable, Range range) throws TableNotFoundException, + AccumuloException, + AccumuloSecurityException { + Scanner scanner = inst.getConnector(creds.getPrincipal(), creds.getToken()).createScanner(metaTable, Authorizations.EMPTY); + TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.fetch(scanner); + scanner.fetchColumnFamily(TabletsSection.FutureLocationColumnFamily.NAME); + scanner.fetchColumnFamily(TabletsSection.CurrentLocationColumnFamily.NAME); + scanner.setRange(range); + return new IsolatedScanner(scanner); + } + @Override public void offline(String tableName) throws AccumuloSecurityException, AccumuloException, TableNotFoundException { offline(tableName, false); http://git-wip-us.apache.org/repos/asf/accumulo/blob/1f674b4e/core/src/test/java/org/apache/accumulo/core/client/impl/TableOperationsImplTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/accumulo/core/client/impl/TableOperationsImplTest.java b/core/src/test/java/org/apache/accumulo/core/client/impl/TableOperationsImplTest.java new file mode 100644 index 0000000..20e068b --- /dev/null +++ b/core/src/test/java/org/apache/accumulo/core/client/impl/TableOperationsImplTest.java @@ -0,0 +1,86 @@ +/* + * 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 + * + * http://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.core.client.impl; + +import java.util.concurrent.TimeUnit; + +import org.apache.accumulo.core.client.Connector; +import org.apache.accumulo.core.client.Instance; +import org.apache.accumulo.core.client.Scanner; +import org.apache.accumulo.core.client.security.tokens.PasswordToken; +import org.apache.accumulo.core.data.KeyExtent; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.metadata.schema.MetadataSchema; +import org.apache.accumulo.core.security.Authorizations; +import org.apache.accumulo.core.security.Credentials; +import org.apache.hadoop.io.Text; +import org.easymock.EasyMock; +import org.junit.Test; + +public class TableOperationsImplTest { + + @Test + public void waitForStoreTransitionScannerConfiguredCorrectly() throws Exception { + final String tableName = "metadata"; + Instance instance = EasyMock.createMock(Instance.class); + Credentials credentials = EasyMock.createMock(Credentials.class); + + TableOperationsImpl topsImpl = new TableOperationsImpl(instance, credentials); + + Connector connector = EasyMock.createMock(Connector.class); + Scanner scanner = EasyMock.createMock(Scanner.class); + + Range range = new KeyExtent(new Text("1"), null, null).toMetadataRange(); + + String user = "root"; + PasswordToken token = new PasswordToken("password"); + + // Credentials expectations + EasyMock.expect(credentials.getPrincipal()).andReturn(user).atLeastOnce(); + EasyMock.expect(credentials.getToken()).andReturn(token).atLeastOnce(); + + // Create the connector and scanner + EasyMock.expect(instance.getConnector(user, token)).andReturn(connector); + EasyMock.expect(connector.createScanner(tableName, Authorizations.EMPTY)).andReturn(scanner); + + // Fetch the columns on the scanner + scanner.fetchColumnFamily(MetadataSchema.TabletsSection.FutureLocationColumnFamily.NAME); + EasyMock.expectLastCall(); + scanner.fetchColumnFamily(MetadataSchema.TabletsSection.CurrentLocationColumnFamily.NAME); + EasyMock.expectLastCall(); + scanner.fetchColumn(MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.getColumnFamily(), + MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN.getColumnQualifier()); + EasyMock.expectLastCall(); + + // Set the Range + scanner.setRange(range); + EasyMock.expectLastCall(); + + // IsolatedScanner -- make the verification pass, not really relevant + EasyMock.expect(scanner.getRange()).andReturn(range).anyTimes(); + EasyMock.expect(scanner.getTimeout(TimeUnit.MILLISECONDS)).andReturn(Long.MAX_VALUE); + EasyMock.expect(scanner.getBatchSize()).andReturn(1000); + EasyMock.expect(scanner.getReadaheadThreshold()).andReturn(100l); + + EasyMock.replay(instance, credentials, connector, scanner); + + topsImpl.createMetadataScanner(instance, credentials, tableName, range); + + EasyMock.verify(instance, credentials, connector, scanner); + } + +}