mirror of https://github.com/apache/cassandra
Improve ProgressLog:
- Schedule a fallback timeout for active requests to ensure progress with lost callbacks - Clear active task for a txnId when new RunInvoker is registered - Consult DurableBefore prior to invoking recovery - Support user invoked reset of a command, clearing any active work and requeueing it - (Testing): Treat progress log for RX as recurring tasks for burn test termination Also: - Add TxnOps RECOVER, FETCH, RESET_PROGRESS_LOG patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20815
This commit is contained in:
parent
54b2f6acbc
commit
f02020f06d
|
|
@ -1 +1 @@
|
|||
Subproject commit 05b2f2415c3e7c109158208824fbc804dfe43f71
|
||||
Subproject commit 20df7e5e5ad7bae442ff6fe4ee716eb7104336c1
|
||||
|
|
@ -121,7 +121,9 @@ public class AccordSpec
|
|||
|
||||
public volatile OptionaldPositiveInt max_queued_loads = OptionaldPositiveInt.UNDEFINED;
|
||||
public volatile OptionaldPositiveInt max_queued_range_loads = OptionaldPositiveInt.UNDEFINED;
|
||||
public volatile OptionaldPositiveInt max_progress_log_concurrency = OptionaldPositiveInt.UNDEFINED;
|
||||
|
||||
public volatile OptionaldPositiveInt progress_log_concurrency = OptionaldPositiveInt.UNDEFINED;
|
||||
public DurationSpec.IntMillisecondsBound progress_log_query_fallback_timeout = new DurationSpec.IntMillisecondsBound("1m");
|
||||
|
||||
public DataStorageSpec.LongMebibytesBound cache_size = null;
|
||||
public DataStorageSpec.LongMebibytesBound working_set_size = null;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import accord.impl.progresslog.DefaultProgressLog;
|
||||
import com.googlecode.concurrenttrees.common.Iterables;
|
||||
import org.apache.cassandra.audit.AuditLogOptions;
|
||||
import org.apache.cassandra.auth.AllowAllInternodeAuthenticator;
|
||||
|
|
@ -189,6 +190,7 @@ public class DatabaseDescriptor
|
|||
private static final int MAX_NUM_TOKENS = 1536;
|
||||
|
||||
private static Config conf;
|
||||
private static DefaultProgressLog.Config accordProgressLogConfig;
|
||||
|
||||
/**
|
||||
* Request timeouts can not be less than below defined value (see CASSANDRA-9375)
|
||||
|
|
@ -561,6 +563,8 @@ public class DatabaseDescriptor
|
|||
|
||||
applyGuardrails();
|
||||
|
||||
applyAccordProgressLog();
|
||||
|
||||
applyStartupChecks();
|
||||
}
|
||||
|
||||
|
|
@ -1297,6 +1301,20 @@ public class DatabaseDescriptor
|
|||
}
|
||||
}
|
||||
|
||||
private static void applyAccordProgressLog()
|
||||
{
|
||||
try
|
||||
{
|
||||
accordProgressLogConfig = new DefaultProgressLog.Config();
|
||||
accordProgressLogConfig.concurrency = conf.accord.progress_log_concurrency.or(32);
|
||||
accordProgressLogConfig.maxActiveRunTime = conf.accord.progress_log_query_fallback_timeout.toDuration();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
throw new ConfigurationException("Invalid accord progress log configuration: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static StartupChecksOptions getStartupChecksOptions()
|
||||
{
|
||||
return startupChecksOptions;
|
||||
|
|
@ -5394,9 +5412,9 @@ public class DatabaseDescriptor
|
|||
return conf.accord.max_queued_range_loads.or(Math.max(4, getAccordConcurrentOps() / 4));
|
||||
}
|
||||
|
||||
public static int getAccordProgressLogMaxConcurrency()
|
||||
public static DefaultProgressLog.Config getAccordProgressLogConfig()
|
||||
{
|
||||
return conf.accord.max_progress_log_concurrency.or(64);
|
||||
return accordProgressLogConfig;
|
||||
}
|
||||
|
||||
public static boolean getAccordCacheShrinkingOn()
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.BoundType;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Sets;
|
||||
|
|
@ -43,6 +46,10 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import accord.api.RoutingKey;
|
||||
import accord.api.TraceEventType;
|
||||
import accord.coordinate.FetchData;
|
||||
import accord.coordinate.FetchRoute;
|
||||
import accord.coordinate.MaybeRecover;
|
||||
import accord.coordinate.RecoverWithRoute;
|
||||
import accord.impl.CommandChange;
|
||||
import accord.impl.progresslog.DefaultProgressLog;
|
||||
import accord.impl.progresslog.TxnStateKind;
|
||||
|
|
@ -50,11 +57,13 @@ import accord.local.Cleanup;
|
|||
import accord.local.Command;
|
||||
import accord.local.CommandStore;
|
||||
import accord.local.CommandStores;
|
||||
import accord.local.CommandStores.LatentStoreSelector;
|
||||
import accord.local.Commands;
|
||||
import accord.local.DurableBefore;
|
||||
import accord.local.LoadKeys;
|
||||
import accord.local.LoadKeysFor;
|
||||
import accord.local.MaxConflicts;
|
||||
import accord.local.Node;
|
||||
import accord.local.PreLoadContext;
|
||||
import accord.local.RejectBefore;
|
||||
import accord.local.SafeCommand;
|
||||
|
|
@ -63,14 +72,21 @@ import accord.local.StoreParticipants;
|
|||
import accord.local.cfk.CommandsForKey;
|
||||
import accord.local.cfk.SafeCommandsForKey;
|
||||
import accord.local.durability.ShardDurability;
|
||||
import accord.primitives.FullRoute;
|
||||
import accord.primitives.Known;
|
||||
import accord.primitives.Participants;
|
||||
import accord.primitives.ProgressToken;
|
||||
import accord.primitives.Route;
|
||||
import accord.primitives.SaveStatus;
|
||||
import accord.primitives.Status;
|
||||
import accord.primitives.Timestamp;
|
||||
import accord.primitives.TxnId;
|
||||
import accord.utils.Invariants;
|
||||
import accord.utils.UnhandledEnum;
|
||||
import accord.utils.async.AsyncChain;
|
||||
import accord.utils.async.AsyncChains;
|
||||
import accord.utils.async.AsyncResult;
|
||||
import accord.utils.async.AsyncResults;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.statements.schema.CreateTableStatement;
|
||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||
|
|
@ -104,6 +120,8 @@ import org.apache.cassandra.service.consensus.migration.TableMigrationState;
|
|||
import org.apache.cassandra.tcm.ClusterMetadata;
|
||||
import org.apache.cassandra.utils.LocalizeString;
|
||||
|
||||
import static accord.api.TraceEventType.RECOVER;
|
||||
import static accord.coordinate.Infer.InvalidIf.NotKnownToBeInvalid;
|
||||
import static accord.local.RedundantStatus.Property.GC_BEFORE;
|
||||
import static accord.local.RedundantStatus.Property.LOCALLY_APPLIED;
|
||||
import static accord.local.RedundantStatus.Property.LOCALLY_DURABLE_TO_COMMAND_STORE;
|
||||
|
|
@ -1201,7 +1219,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
|
|||
public static final class TxnOpsTable extends AbstractMutableVirtualTable implements AbstractVirtualTable.DataSet
|
||||
{
|
||||
// TODO (expected): test each of these operations
|
||||
enum Op { ERASE_VESTIGIAL, INVALIDATE, TRY_EXECUTE, FORCE_APPLY, FORCE_UPDATE }
|
||||
enum Op { ERASE_VESTIGIAL, INVALIDATE, TRY_EXECUTE, FORCE_APPLY, FORCE_UPDATE, RECOVER, FETCH, RESET_PROGRESS_LOG }
|
||||
private TxnOpsTable()
|
||||
{
|
||||
super(parse(VIRTUAL_ACCORD_DEBUG, TXN_OPS,
|
||||
|
|
@ -1209,7 +1227,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
|
|||
"CREATE TABLE %s (\n" +
|
||||
" txn_id text,\n" +
|
||||
" command_store_id int,\n" +
|
||||
" op text,\n" +
|
||||
" op text," +
|
||||
" PRIMARY KEY (txn_id, command_store_id)" +
|
||||
')', UTF8Type.instance));
|
||||
}
|
||||
|
|
@ -1279,6 +1297,76 @@ public class AccordDebugKeyspace extends VirtualKeyspace
|
|||
return Commands.applyChain(safeStore, (Command.Executed) command);
|
||||
});
|
||||
break;
|
||||
case FETCH:
|
||||
runWithRoute(txnId, commandStoreId, command -> {
|
||||
Timestamp executeAt = command.executeAtIfKnown();
|
||||
return (route, result) -> fetch(txnId, executeAt, route, result);
|
||||
});
|
||||
break;
|
||||
case RECOVER:
|
||||
runWithRoute(txnId, commandStoreId, command -> (route, result) -> {
|
||||
recover(txnId, route, result);
|
||||
});
|
||||
break;
|
||||
case RESET_PROGRESS_LOG:
|
||||
run(txnId, commandStoreId, safeStore -> {
|
||||
((DefaultProgressLog)safeStore.progressLog()).requeue(safeStore, TxnStateKind.Waiting, txnId);
|
||||
((DefaultProgressLog)safeStore.progressLog()).requeue(safeStore, TxnStateKind.Home, txnId);
|
||||
return AsyncChains.success(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void runWithRoute(TxnId txnId, int commandStoreId, Function<Command, BiConsumer<Route<?>, AsyncResult.Settable<Void>>> apply)
|
||||
{
|
||||
run(txnId, commandStoreId, safeStore -> {
|
||||
SafeCommand safeCommand = safeStore.unsafeGet(txnId);
|
||||
Command command = safeCommand.current();
|
||||
if (command == null)
|
||||
throw new InvalidRequestException(txnId + " not known");
|
||||
Node node = AccordService.instance().node();
|
||||
AsyncResult.Settable<Void> result = new AsyncResults.SettableResult<>();
|
||||
BiConsumer<Route<?>, AsyncResult.Settable<Void>> consumer = apply.apply(command);
|
||||
if (command.route() == null)
|
||||
{
|
||||
FetchRoute.fetchRoute(node, txnId, command.maxContactable(), LatentStoreSelector.standard(), (success, fail) -> {
|
||||
if (fail != null) result.setFailure(fail);
|
||||
else consumer.accept(success, result);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
consumer.accept(command.route(), result);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private void fetch(TxnId txnId, Timestamp executeAtIfKnown, Route<?> route, AsyncResult.Settable<Void> result)
|
||||
{
|
||||
Node node = AccordService.instance().node();
|
||||
FetchData.fetchSpecific(Known.Apply, node, txnId, executeAtIfKnown, route, route.withHomeKey(), LatentStoreSelector.standard(), (success, fail) -> {
|
||||
if (fail != null) result.setFailure(fail);
|
||||
else result.setSuccess(null);
|
||||
});
|
||||
}
|
||||
|
||||
private void recover(TxnId txnId, @Nullable Route<?> route, AsyncResult.Settable<Void> result)
|
||||
{
|
||||
Node node = AccordService.instance().node();
|
||||
if (Route.isFullRoute(route))
|
||||
{
|
||||
RecoverWithRoute.recover(node, node.someSequentialExecutor(), txnId, NotKnownToBeInvalid, (FullRoute<?>) route, null, LatentStoreSelector.standard(), (success, fail) -> {
|
||||
if (fail != null) result.setFailure(fail);
|
||||
else result.setSuccess(null);
|
||||
}, node.agent().trace(txnId, RECOVER));
|
||||
}
|
||||
else
|
||||
{
|
||||
MaybeRecover.maybeRecover(node, txnId, NotKnownToBeInvalid, route, ProgressToken.NONE, LatentStoreSelector.standard(), (success, fail) -> {
|
||||
if (fail != null) result.setFailure(fail);
|
||||
else result.setSuccess(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -181,8 +181,8 @@ public class AccordCommandStore extends CommandStore
|
|||
this.journal = journal;
|
||||
this.rangeSearcher = RangeSearcher.extractRangeSearcher(journal);
|
||||
this.sharedExecutor = sharedExecutor;
|
||||
if (this.progressLog() instanceof DefaultProgressLog)
|
||||
((DefaultProgressLog)this.progressLog).setMaxConcurrency(DatabaseDescriptor.getAccordProgressLogMaxConcurrency());
|
||||
if (this.progressLog instanceof DefaultProgressLog)
|
||||
((DefaultProgressLog)this.progressLog).unsafeSetConfig(DatabaseDescriptor.getAccordProgressLogConfig());
|
||||
|
||||
final AccordCache.Type<TxnId, Command, AccordSafeCommand>.Instance commands;
|
||||
final AccordCache.Type<RoutingKey, CommandsForKey, AccordSafeCommandsForKey>.Instance commandsForKey;
|
||||
|
|
|
|||
Loading…
Reference in New Issue