Search in sources :

Example 21 with ExternalCompactionId

use of org.apache.accumulo.core.metadata.schema.ExternalCompactionId in project accumulo by apache.

the class ExternalCompaction_2_IT method testExternalCompactionsSucceedsRunWithTableOffline.

@Test
public void testExternalCompactionsSucceedsRunWithTableOffline() throws Exception {
    getCluster().getClusterControl().stop(ServerType.COMPACTION_COORDINATOR);
    getCluster().getClusterControl().stop(ServerType.COMPACTOR);
    String table1 = this.getUniqueNames(1)[0];
    try (AccumuloClient client = Accumulo.newClient().from(getCluster().getClientProperties()).build()) {
        createTable(client, table1, "cs2");
        // set compaction ratio to 1 so that majc occurs naturally, not user compaction
        // user compaction blocks merge
        client.tableOperations().setProperty(table1, Property.TABLE_MAJC_RATIO.toString(), "1.0");
        // cause multiple rfiles to be created
        writeData(client, table1);
        writeData(client, table1);
        writeData(client, table1);
        writeData(client, table1);
        getCluster().getClusterControl().startCoordinator(TestCompactionCoordinatorForOfflineTable.class);
        TableId tid = getCluster().getServerContext().getTableId(table1);
        // Confirm that no final state is in the metadata table
        assertEquals(0, getFinalStatesForTable(getCluster(), tid).count());
        // Offline the table when the compaction starts
        final AtomicBoolean succeededInTakingOffline = new AtomicBoolean(false);
        Thread t = new Thread(() -> {
            try (AccumuloClient client2 = Accumulo.newClient().from(getCluster().getClientProperties()).build()) {
                TExternalCompactionList metrics2 = getRunningCompactions(getCluster().getServerContext());
                while (metrics2.getCompactions() == null) {
                    metrics2 = getRunningCompactions(getCluster().getServerContext());
                    if (metrics2.getCompactions() == null) {
                        UtilWaitThread.sleep(50);
                    }
                }
                LOG.info("Taking table offline");
                client2.tableOperations().offline(table1, false);
                succeededInTakingOffline.set(true);
            } catch (Exception e) {
                LOG.error("Error: ", e);
            }
        });
        t.start();
        // Start the compactor
        getCluster().getClusterControl().startCompactors(Compactor.class, 1, QUEUE2);
        // Wait for the compaction to start by waiting for 1 external compaction column
        Set<ExternalCompactionId> ecids = ExternalCompactionTestUtils.waitForCompactionStartAndReturnEcids(getCluster().getServerContext(), tid);
        // Confirm that this ECID shows up in RUNNING set
        int matches = ExternalCompactionTestUtils.confirmCompactionRunning(getCluster().getServerContext(), ecids);
        assertTrue(matches > 0);
        t.join();
        if (!succeededInTakingOffline.get()) {
            fail("Failed to offline table");
        }
        confirmCompactionCompleted(getCluster().getServerContext(), ecids, TCompactionState.SUCCEEDED);
        // Confirm that final state is in the metadata table
        assertEquals(1, getFinalStatesForTable(getCluster(), tid).count());
        // Online the table
        client.tableOperations().online(table1);
        // wait for compaction to be committed by tserver or test timeout
        long finalStateCount = getFinalStatesForTable(getCluster(), tid).count();
        while (finalStateCount > 0) {
            finalStateCount = getFinalStatesForTable(getCluster(), tid).count();
            if (finalStateCount > 0) {
                UtilWaitThread.sleep(50);
            }
        }
        // We need to cancel the compaction or delete the table here because we initiate a user
        // compaction above in the test. Even though the external compaction was cancelled
        // because we split the table, FaTE will continue to queue up a compaction
        client.tableOperations().delete(table1);
        getCluster().getClusterControl().stop(ServerType.COMPACTION_COORDINATOR);
        getCluster().getClusterControl().stop(ServerType.COMPACTOR);
    }
}
Also used : AccumuloClient(org.apache.accumulo.core.client.AccumuloClient) TableId(org.apache.accumulo.core.data.TableId) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) TExternalCompactionList(org.apache.accumulo.core.compaction.thrift.TExternalCompactionList) ExternalCompactionId(org.apache.accumulo.core.metadata.schema.ExternalCompactionId) UtilWaitThread(org.apache.accumulo.fate.util.UtilWaitThread) Test(org.junit.Test)

Example 22 with ExternalCompactionId

use of org.apache.accumulo.core.metadata.schema.ExternalCompactionId in project accumulo by apache.

the class CompactableImpl method processExternalMetadata.

/**
 * This method validates metadata about external compactions. It also extracts specific
 * information needed for user and selector compactions.
 */
static Optional<SelectedInfo> processExternalMetadata(Map<ExternalCompactionId, ExternalCompactionMetadata> extCompactions, Supplier<Optional<Long>> tabletCompactionId, Set<StoredTabletFile> tabletFiles, Map<ExternalCompactionId, String> externalCompactionsToRemove) {
    // Check that external compactions have disjoint sets of files. Also check that each external
    // compaction only has files inside a tablet.
    Set<StoredTabletFile> seen = new HashSet<>();
    boolean overlap = false;
    for (var entry : extCompactions.entrySet()) {
        ExternalCompactionMetadata ecMeta = entry.getValue();
        if (!tabletFiles.containsAll(ecMeta.getJobFiles())) {
            externalCompactionsToRemove.putIfAbsent(entry.getKey(), "Has files outside of tablet files");
        } else if (!Collections.disjoint(seen, ecMeta.getJobFiles())) {
            overlap = true;
        }
        seen.addAll(ecMeta.getJobFiles());
    }
    if (overlap) {
        extCompactions.keySet().forEach(ecid -> {
            externalCompactionsToRemove.putIfAbsent(ecid, "Some external compaction files overlap");
        });
        return Optional.empty();
    }
    /*
     * The rest of the code validates user compaction metadata and extracts needed information.
     *
     * For user compactions a set of files is selected. Those files then get compacted by one or
     * more compactions until the set is empty. This method attempts to reconstruct the selected set
     * of files when a tablet is loaded with an external user compaction. It avoids repeating work
     * and when a user compaction completes, files are verified against the selected set. Since the
     * data is coming from persisted storage, lots of checks are done in this method rather than
     * assuming the persisted data is correct.
     */
    CompactionKind extKind = null;
    boolean unexpectedExternal = false;
    Set<StoredTabletFile> tmpSelectedFiles = null;
    Boolean initiallySelAll = null;
    Long cid = null;
    Boolean propDel = null;
    int count = 0;
    ArrayList<String> reasons = new ArrayList<>();
    for (Entry<ExternalCompactionId, ExternalCompactionMetadata> entry : extCompactions.entrySet()) {
        var ecMeta = entry.getValue();
        if (ecMeta.getKind() != CompactionKind.USER && ecMeta.getKind() != CompactionKind.SELECTOR) {
            continue;
        }
        count++;
        if (extKind == null || extKind == ecMeta.getKind()) {
            extKind = ecMeta.getKind();
        } else {
            reasons.add("Saw USER and SELECTOR");
            unexpectedExternal = true;
            break;
        }
        if (tmpSelectedFiles == null) {
            tmpSelectedFiles = Sets.union(ecMeta.getJobFiles(), ecMeta.getNextFiles());
        } else if (!Sets.union(ecMeta.getJobFiles(), ecMeta.getNextFiles()).equals(tmpSelectedFiles)) {
            reasons.add("Selected set of files differs");
            unexpectedExternal = true;
            break;
        }
        if (initiallySelAll == null) {
            initiallySelAll = ecMeta.getInitiallySelecteAll();
        } else if (initiallySelAll != ecMeta.getInitiallySelecteAll()) {
            unexpectedExternal = true;
            reasons.add("Disagreement on selectedAll");
            break;
        }
        if (ecMeta.getKind() == CompactionKind.USER) {
            if (ecMeta.getCompactionId() == null) {
                unexpectedExternal = true;
                reasons.add("Missing compactionId");
                break;
            } else if (cid == null) {
                cid = ecMeta.getCompactionId();
            } else if (!cid.equals(ecMeta.getCompactionId())) {
                unexpectedExternal = true;
                reasons.add("Disagreement on compactionId");
                break;
            }
        } else if (ecMeta.getCompactionId() != null) {
            unexpectedExternal = true;
            reasons.add("Unexpected compactionId");
            break;
        }
        if (propDel == null) {
            propDel = ecMeta.getPropagateDeletes();
        } else if (propDel != ecMeta.getPropagateDeletes()) {
            unexpectedExternal = true;
            reasons.add("Disagreement on propagateDeletes");
            break;
        }
    }
    if (propDel != null && !propDel && count > 1) {
        unexpectedExternal = true;
        reasons.add("Concurrent compactions not propagatingDeletes");
    }
    if (extKind == CompactionKind.USER) {
        Optional<Long> compactionId = tabletCompactionId.get();
        if (compactionId.isEmpty()) {
            unexpectedExternal = true;
            reasons.add("No compaction id in zookeeper");
        } else if (!compactionId.get().equals(cid)) {
            unexpectedExternal = true;
            reasons.add("Compaction id mismatch with zookeeper");
        }
    }
    if (unexpectedExternal) {
        String reason = reasons.toString();
        extCompactions.entrySet().stream().filter(e -> {
            var kind = e.getValue().getKind();
            return kind == CompactionKind.SELECTOR || kind == CompactionKind.USER;
        }).map(Entry::getKey).forEach(ecid -> externalCompactionsToRemove.putIfAbsent(ecid, reason));
        return Optional.empty();
    }
    if (extKind != null)
        return Optional.of(new SelectedInfo(initiallySelAll, tmpSelectedFiles, extKind));
    return Optional.empty();
}
Also used : ExternalCompactionId(org.apache.accumulo.core.metadata.schema.ExternalCompactionId) ArrayList(java.util.ArrayList) ExternalCompactionMetadata(org.apache.accumulo.core.metadata.schema.ExternalCompactionMetadata) CompactionKind(org.apache.accumulo.core.spi.compaction.CompactionKind) AtomicLong(java.util.concurrent.atomic.AtomicLong) StoredTabletFile(org.apache.accumulo.core.metadata.StoredTabletFile) HashSet(java.util.HashSet)

Aggregations

ExternalCompactionId (org.apache.accumulo.core.metadata.schema.ExternalCompactionId)22 Test (org.junit.Test)13 ArrayList (java.util.ArrayList)9 AccumuloClient (org.apache.accumulo.core.client.AccumuloClient)8 TableId (org.apache.accumulo.core.data.TableId)8 TExternalCompactionList (org.apache.accumulo.core.compaction.thrift.TExternalCompactionList)6 TExternalCompactionJob (org.apache.accumulo.core.tabletserver.thrift.TExternalCompactionJob)6 ServerContext (org.apache.accumulo.server.ServerContext)6 HashSet (java.util.HashSet)4 TExternalCompaction (org.apache.accumulo.core.compaction.thrift.TExternalCompaction)4 AccumuloConfiguration (org.apache.accumulo.core.conf.AccumuloConfiguration)4 TabletsMetadata (org.apache.accumulo.core.metadata.schema.TabletsMetadata)4 HostAndPort (org.apache.accumulo.core.util.HostAndPort)4 AbstractServer (org.apache.accumulo.server.AbstractServer)4 ServerAddress (org.apache.accumulo.server.rpc.ServerAddress)4 Text (org.apache.hadoop.io.Text)4 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)4 IOException (java.io.IOException)3 Collections (java.util.Collections)3 Set (java.util.Set)3