Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
write once delete once init (#6634)
Browse files Browse the repository at this point in the history
Adds write-once-delete-once workflow
  • Loading branch information
Sam-Kramer authored Jul 10, 2023
1 parent d58fddd commit 9af0e98
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 12 deletions.
4 changes: 2 additions & 2 deletions atlasdb-workload-server-antithesis/var/compact.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

##
# (c) Copyright 2023 Palantir Technologies Inc. All rights reserved.
Expand All @@ -16,4 +16,4 @@
# limitations under the License.
#

podman exec $1 bash -c 'palantir-cassandra-*/bin/nodetool compact'¡
podman exec $1 bash -c 'palantir-cassandra-*/bin/nodetool compact'
6 changes: 3 additions & 3 deletions atlasdb-workload-server-antithesis/var/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '2'

services:
cassandra1:
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc5
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc8
ports:
- "9160"
- "9042"
Expand All @@ -20,7 +20,7 @@ services:
ipv4_address: 10.20.20.2

cassandra2:
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc5
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc8
ports:
- "9160"
- "9042"
Expand All @@ -38,7 +38,7 @@ services:
ipv4_address: 10.20.20.3

cassandra3:
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc5
image: palantirtechnologies/cassandra:2.2.18-1.112.0-rc8
ports:
- "9160"
- "9042"
Expand Down
2 changes: 1 addition & 1 deletion atlasdb-workload-server-antithesis/var/flush.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash

##
# (c) Copyright 2023 Palantir Technologies Inc. All rights reserved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import com.palantir.atlasdb.workload.workflow.Workflow;
import com.palantir.atlasdb.workload.workflow.WorkflowAndInvariants;
import com.palantir.atlasdb.workload.workflow.WorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.WriteOnceDeleteOnceWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.WriteOnceDeleteOnceWorkflows;
import com.palantir.atlasdb.workload.workflow.bank.BankBalanceWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.bank.BankBalanceWorkflows;
import com.palantir.atlasdb.workload.workflow.ring.RingWorkflowConfiguration;
Expand Down Expand Up @@ -119,7 +121,7 @@ private void scheduleBackgroundJobs(Environment environment) {
AntithesisCassandraSidecarResource.INSTANCE,
DefaultBuggifyFactory.INSTANCE),
0,
10,
2,
TimeUnit.SECONDS);
}

Expand Down Expand Up @@ -150,6 +152,8 @@ private void runWorkflows(WorkloadServerConfiguration configuration, Environment
configuration.install().bankBalanceConfig();
RandomWorkflowConfiguration randomWorkflowConfig =
configuration.install().randomConfig();
WriteOnceDeleteOnceWorkflowConfiguration writeOnceDeleteOnceConfig =
configuration.install().writeOnceDeleteOnceConfig();

waitForTransactionStoreFactoryToBeInitialized(transactionStoreFactory);

Expand All @@ -168,7 +172,9 @@ private void runWorkflows(WorkloadServerConfiguration configuration, Environment
singleBusyCellReadNoTouchWorkflowConfiguration,
environment.lifecycle()),
createBankBalanceWorkflow(transactionStoreFactory, bankBalanceConfig, environment.lifecycle()),
createRandomWorkflow(transactionStoreFactory, randomWorkflowConfig, environment.lifecycle()));
createRandomWorkflow(transactionStoreFactory, randomWorkflowConfig, environment.lifecycle()),
createWriteOnceDeleteOnceWorkflow(
transactionStoreFactory, writeOnceDeleteOnceConfig, environment.lifecycle()));

log.info("antithesis: terminate");

Expand Down Expand Up @@ -355,6 +361,27 @@ private WorkflowAndInvariants<Workflow> createRandomWorkflow(
.build();
}

private WorkflowAndInvariants<Workflow> createWriteOnceDeleteOnceWorkflow(
AtlasDbTransactionStoreFactory transactionStoreFactory,
WriteOnceDeleteOnceWorkflowConfiguration workflowConfig,
LifecycleEnvironment lifecycle) {
ExecutorService executorService =
createExecutorService(workflowConfig, lifecycle, WriteOnceDeleteOnceWorkflows.class);
InteractiveTransactionStore transactionStore = transactionStoreFactory.create(
Map.of(
workflowConfig.tableConfiguration().tableName(),
workflowConfig.tableConfiguration().isolationLevel()),
Set.of());
return WorkflowAndInvariants.builder()
.workflow(WriteOnceDeleteOnceWorkflows.create(
transactionStore, workflowConfig, MoreExecutors.listeningDecorator(executorService)))
.addInvariantReporters(new DurableWritesInvariantMetricReporter(
WriteOnceDeleteOnceWorkflows.class.getSimpleName(),
DurableWritesMetrics.of(taggedMetricRegistry)))
.addInvariantReporters(SerializableInvariantLogReporter.INSTANCE)
.build();
}

@VisibleForTesting
CountDownLatch workflowsRanLatch() {
return workflowsRanLatch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.palantir.atlasdb.workload.workflow.SingleBusyCellWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.SingleRowTwoCellsWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.TransientRowsWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.WriteOnceDeleteOnceWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.bank.BankBalanceWorkflowConfiguration;
import com.palantir.atlasdb.workload.workflow.ring.RingWorkflowConfiguration;
import org.immutables.value.Value;
Expand All @@ -48,5 +49,7 @@ public interface WorkloadServerInstallConfiguration {

SingleBusyCellReadNoTouchWorkflowConfiguration singleBusyCellReadsNoTouchConfig();

WriteOnceDeleteOnceWorkflowConfiguration writeOnceDeleteOnceConfig();

boolean exitAfterRunning();
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@ install:
iterationCount: 100
type: single-busy-cell-reads-no-touch
maxThreadCount: 2
writeOnceDeleteOnceConfig:
tableConfiguration:
tableName: write-once-delete-once-test
isolationLevel: SERIALIZABLE
iterationCount: 100
type: write-once-delete-once
exitAfterRunning: false
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ install:
password: cassandra
ssl: false
replicationFactor: 3
gcGraceSeconds: 300
gcGraceSeconds: 60
targetedSweep:
enableSweepQueueWrites: true
namespace: workload_server
Expand Down Expand Up @@ -85,4 +85,10 @@ install:
iterationCount: 100
type: single-busy-cell-reads-no-touch
maxThreadCount: 2
writeOnceDeleteOnceConfig:
tableConfiguration:
tableName: write_once_delete_once_test
isolationLevel: SERIALIZABLE
iterationCount: 100
type: write-once-delete-once
exitAfterRunning: true
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ install:
iterationCount: 100
type: single-busy-cell-reads-no-touch
maxThreadCount: 2
writeOnceDeleteOnceConfig:
tableConfiguration:
tableName: write-once-delete-once-test
isolationLevel: SERIALIZABLE
iterationCount: 100
type: write-once-delete-once
exitAfterRunning: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* (c) Copyright 2023 Palantir Technologies Inc. All rights reserved.
*
* Licensed 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 com.palantir.atlasdb.workload.workflow;

import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.util.concurrent.RateLimiter;
import org.immutables.value.Value;

@Value.Immutable
@JsonSerialize(as = ImmutableWriteOnceDeleteOnceWorkflowConfiguration.class)
@JsonDeserialize(as = ImmutableWriteOnceDeleteOnceWorkflowConfiguration.class)
@JsonTypeName(WriteOnceDeleteOnceWorkflowConfiguration.TYPE)
public interface WriteOnceDeleteOnceWorkflowConfiguration extends WorkflowConfiguration {
String TYPE = "write-once-delete-once";

TableConfiguration tableConfiguration();

/**
* The maximum value (exclusive) for the row key.
* This is half the iteration count, so we ideally perform two actions per cell.
*/
@Value.Derived
default Integer maxKey() {
return iterationCount() / 2;
}

@Value.Lazy
default RateLimiter transactionRateLimiter() {
return RateLimiter.create(rateLimit());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* (c) Copyright 2023 Palantir Technologies Inc. All rights reserved.
*
* Licensed 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 com.palantir.atlasdb.workload.workflow;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.palantir.atlasdb.buggify.api.BuggifyFactory;
import com.palantir.atlasdb.buggify.impl.DefaultBuggifyFactory;
import com.palantir.atlasdb.buggify.impl.DefaultNativeSamplingSecureRandomFactory;
import com.palantir.atlasdb.workload.store.ImmutableWorkloadCell;
import com.palantir.atlasdb.workload.store.InteractiveTransactionStore;
import com.palantir.atlasdb.workload.store.WorkloadCell;
import com.palantir.atlasdb.workload.transaction.witnessed.WitnessedTransaction;
import java.security.SecureRandom;
import java.util.Optional;
import java.util.function.IntFunction;

/**
* The idea of this class is to catch bugs around tombstone drops, by having a
* workflow which _mostly_ writes once first and then on deletes once for a given cell.
* Probability and randomness are used to enforce this behaviour, thus it's possible to have a write without a
* delete, or visa versa; some cells may randomly be written or deleted multiple times as well.
*/
public final class WriteOnceDeleteOnceWorkflows {

private static final SecureRandom RANDOM = DefaultNativeSamplingSecureRandomFactory.INSTANCE.create();

private static final Integer COLUMN = 0;

private WriteOnceDeleteOnceWorkflows() {
// Utility class
}

public static Workflow create(
InteractiveTransactionStore store,
WriteOnceDeleteOnceWorkflowConfiguration configuration,
ListeningExecutorService executionExecutor) {
return create(store, configuration, executionExecutor, DefaultBuggifyFactory.INSTANCE, RANDOM::nextInt);
}

@VisibleForTesting
static Workflow create(
InteractiveTransactionStore store,
WriteOnceDeleteOnceWorkflowConfiguration configuration,
ListeningExecutorService executionExecutor,
BuggifyFactory buggifyFactory,
IntFunction<Integer> randomNumberGenerator) {
WriteOnceDeleteOnceWorkflow workflow =
new WriteOnceDeleteOnceWorkflow(buggifyFactory, randomNumberGenerator, configuration);
return DefaultWorkflow.create(store, workflow::run, configuration, executionExecutor);
}

private static class WriteOnceDeleteOnceWorkflow {
private final BuggifyFactory buggifyFactory;
private final IntFunction<Integer> rowKeyGenerator;
private final WriteOnceDeleteOnceWorkflowConfiguration configuration;

public WriteOnceDeleteOnceWorkflow(
BuggifyFactory buggifyFactory,
IntFunction<Integer> rowKeyGenerator,
WriteOnceDeleteOnceWorkflowConfiguration configuration) {
this.buggifyFactory = buggifyFactory;
this.rowKeyGenerator = rowKeyGenerator;
this.configuration = configuration;
}

public Optional<WitnessedTransaction> run(InteractiveTransactionStore store, Integer taskIndex) {
configuration.transactionRateLimiter().acquire();
return store.readWrite(txn -> {
Integer rowKey = rowKeyGenerator.apply(configuration.maxKey());
if (buggifyFactory
.maybe(calculateChanceForDelete(taskIndex, configuration.iterationCount()))
.asBoolean()) {
txn.delete(configuration.tableConfiguration().tableName(), cell(rowKey));
} else {
txn.write(configuration.tableConfiguration().tableName(), cell(rowKey), RANDOM.nextInt(10000));
}
});
}
}

@VisibleForTesting
static Double calculateChanceForDelete(Integer taskIndex, Integer iterationCount) {
// Index generation is [inclusive, exclusive], so we need to add 1 to the task index.
return (taskIndex + 1.0) / iterationCount;
}

@VisibleForTesting
static WorkloadCell cell(Integer index) {
return ImmutableWorkloadCell.of(index, COLUMN);
}
}
Loading

0 comments on commit 9af0e98

Please sign in to comment.