Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IGNITE-17811 Efficiently unlock all transaction locks #2720

Closed
wants to merge 51 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
cc7cf98
IGNITE-17811 Implement bounded lock table
ascherbakoff Oct 18, 2023
3f57ac2
IGNITE-17811 Fix tests
ascherbakoff Oct 19, 2023
87ab7ab
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Oct 19, 2023
17dc1a3
IGNITE-17811 Cleanup
ascherbakoff Oct 19, 2023
009c910
IGNITE-17811 Removed untrack
ascherbakoff Oct 19, 2023
b0d0c43
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Oct 19, 2023
aa49260
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Oct 19, 2023
8524559
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Oct 30, 2023
1a23d3c
IGNITE-17811 Fix comment
ascherbakoff Oct 30, 2023
4207569
IGNITE-17811 Temporary disable suspicious test
ascherbakoff Oct 30, 2023
a1c1b35
IGNITE-17811 Fix memory leak
ascherbakoff Oct 30, 2023
34e3286
IGNITE-17811 Fix compilation
ascherbakoff Oct 30, 2023
8ffd2fd
IGNITE-17811 Leak fix attempt
ascherbakoff Oct 31, 2023
5c363e8
IGNITE-17811 Bounded lock table
ascherbakoff Nov 10, 2023
effb4b0
IGNITE-17811 Fix NPE
ascherbakoff Nov 12, 2023
70e21b9
IGNITE-17811 Fix compilation
ascherbakoff Nov 13, 2023
4411104
IGNITE-17811 Fix lock table and leak in tx test
ascherbakoff Nov 14, 2023
a021853
IGNITE-17811 Fix NPE
ascherbakoff Nov 14, 2023
65a6085
IGNITE-17811 Fix test and formatting
ascherbakoff Nov 17, 2023
459597b
IGNITE-17811 Fix unlock issue
ascherbakoff Nov 17, 2023
1b9a0fc
IGNITE-17811 Fix formatting
ascherbakoff Nov 20, 2023
fa665c6
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Nov 20, 2023
34b2aa8
IGNITE-17811 Merge with master
ascherbakoff Nov 20, 2023
c48662a
IGNITE-17811 Fix test
ascherbakoff Nov 20, 2023
fc4a79c
IGNITE-17811 Fix javadoc
ascherbakoff Nov 20, 2023
0026433
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Nov 20, 2023
9532861
IGNITE-17811 Add test for unbounded heap lock manager, fix compilation
ascherbakoff Nov 23, 2023
580a38a
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Nov 24, 2023
e5ad551
IGNITE-17811 Review fixes
ascherbakoff Nov 28, 2023
93fcfca
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Nov 28, 2023
6d087d8
IGNITE-17811 Fix compilation
ascherbakoff Nov 28, 2023
dcecaa0
IGNITE-17811 Fix compilation again
ascherbakoff Nov 29, 2023
6bf6764
IGNITE-17811 Fix tests
ascherbakoff Nov 29, 2023
d39d76e
IGNITE-17811 Fix tests again
ascherbakoff Nov 29, 2023
bd5bbac
IGNITE-17811 Suppress warnings
ascherbakoff Nov 29, 2023
c37ce6a
IGNITE-17811 Suppress warnings
ascherbakoff Nov 29, 2023
e00a2e3
IGNITE-17811 Suppress warnings
ascherbakoff Nov 30, 2023
5667c94
IGNITE-17811 Move mock clearing to the safe place
ascherbakoff Nov 30, 2023
54324b0
IGNITE-17811 Remove commented code
ascherbakoff Nov 30, 2023
339570b
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Dec 1, 2023
d42f20f
IGNITE-17811 Add event support to unbounded lock manager
ascherbakoff Dec 1, 2023
cce073b
IGNITE-17811 Fix compilation
ascherbakoff Dec 1, 2023
eb23f6e
IGNITE-17811 Fix review comments
ascherbakoff Dec 8, 2023
883acf7
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Dec 8, 2023
38e6541
IGNITE-17811 Fix review comments
ascherbakoff Dec 8, 2023
2563ded
IGNITE-17811 Fix compilation
ascherbakoff Dec 8, 2023
833a8d1
IGNITE-17811 Cleanup
ascherbakoff Dec 10, 2023
db32922
Merge branch 'main' of https://gitbox.apache.org/repos/asf/ignite-3 i…
ascherbakoff Dec 13, 2023
d2de790
IGNITE-17811 Removed test
ascherbakoff Dec 13, 2023
d5e0596
IGNITE-17811 Fix attempt
ascherbakoff Dec 15, 2023
fa7262f
IGNITE-17811 Fix style
ascherbakoff Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/platforms/cpp/tests/odbc-test/transaction_test.cpp
Expand Up @@ -575,7 +575,7 @@ TEST_F(transaction_test, transaction_error) {
try {
insert_test_value(conn2.m_statement, 2, "test_2");
} catch (const odbc_exception &err) {
EXPECT_THAT(err.message, testing::HasSubstr("Failed to acquire a lock due to a conflict"));
EXPECT_THAT(err.message, testing::HasSubstr("Failed to acquire a lock due to a possible deadlock"));
// TODO: IGNITE-19944 Propagate SQL errors from engine to driver
EXPECT_EQ(err.sql_state, "HY000");
throw;
Expand Down
@@ -0,0 +1,119 @@
/*
* 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.ignite.internal.benchmark;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.ignite.internal.TestHybridClock;
import org.apache.ignite.internal.hlc.HybridClock;
import org.apache.ignite.internal.storage.RowId;
import org.apache.ignite.internal.tx.LockKey;
import org.apache.ignite.internal.tx.LockManager;
import org.apache.ignite.internal.tx.LockMode;
import org.apache.ignite.internal.tx.impl.HeapLockManager;
import org.apache.ignite.internal.tx.impl.TransactionIdGenerator;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

/**
* Benchmark lock manager.
*/
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class LockManagerBenchmark {
private LockManager lockManager;
private TransactionIdGenerator generator;
private HybridClock clock;

/**
* Initializes session and statement.
*/
@Setup
public void setUp() {
lockManager = new HeapLockManager();
generator = new TransactionIdGenerator(0);
clock = new TestHybridClock(() -> 0L);
}

/**
* Closes resources.
*/
@TearDown
public void tearDown() throws Exception {
if (!lockManager.isEmpty()) {
throw new AssertionError("Invalid lock manager state");
}
}

/**
* Concurrent active transactions.
*/
@Param({"200"})
private int concTxns;

/**
* Take and release some locks.
*/
@Benchmark
@Warmup(iterations = 1, time = 3)
@Measurement(iterations = 1, time = 10)
public void lockCommit() {
List<UUID> ids = new ArrayList<>(concTxns);

int c = 0;

for (int i = 0; i < concTxns; i++) {
UUID txId = generator.transactionIdFor(clock.now());
ids.add(txId);
lockManager.acquire(txId, new LockKey(0, new RowId(0, new UUID(0, c++))), LockMode.X).join();
sanpwc marked this conversation as resolved.
Show resolved Hide resolved
}

for (UUID id : ids) {
lockManager.releaseAll(id);
}
}

/**
* Benchmark's entry point.
*/
public static void main(String[] args) throws RunnerException {
// TODO JVM args
Options opt = new OptionsBuilder()
.include(".*" + LockManagerBenchmark.class.getSimpleName() + ".*")
.forks(1)
.threads(1)
.mode(Mode.AverageTime)
.build();

new Runner(opt).run();
}
}
@@ -0,0 +1,235 @@
/*
* 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.ignite.distributed;

import static org.apache.ignite.internal.replicator.ReplicaManager.DEFAULT_IDLE_SAFE_TIME_PROPAGATION_PERIOD_MILLISECONDS;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
import org.apache.ignite.internal.hlc.HybridClock;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.placementdriver.PlacementDriver;
import org.apache.ignite.internal.raft.configuration.RaftConfiguration;
import org.apache.ignite.internal.replicator.ReplicaService;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.configuration.GcConfiguration;
import org.apache.ignite.internal.table.TableViewInternal;
import org.apache.ignite.internal.testframework.IgniteAbstractTest;
import org.apache.ignite.internal.tx.DeadlockPreventionPolicy;
import org.apache.ignite.internal.tx.HybridTimestampTracker;
import org.apache.ignite.internal.tx.configuration.TransactionConfiguration;
import org.apache.ignite.internal.tx.impl.HeapLockManager;
import org.apache.ignite.internal.tx.impl.HeapLockManager.LockState;
import org.apache.ignite.internal.tx.impl.HeapUnboundedLockManager;
import org.apache.ignite.internal.tx.impl.TransactionIdGenerator;
import org.apache.ignite.internal.tx.impl.TxManagerImpl;
import org.apache.ignite.internal.type.NativeTypes;
import org.apache.ignite.network.ClusterNode;
import org.apache.ignite.network.ClusterService;
import org.apache.ignite.raft.jraft.test.TestUtils;
import org.apache.ignite.table.RecordView;
import org.apache.ignite.table.Tuple;
import org.apache.ignite.tx.Transaction;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;

/**
* Test lock table.
*/
@ExtendWith(ConfigurationExtension.class)
public class ItLockTableTest extends IgniteAbstractTest {
private static final IgniteLogger LOG = Loggers.forClass(ItLockTableTest.class);

private static int EMP_TABLE_ID = 2;

private static final int CACHE_SIZE = 10;

private static final String TABLE_NAME = "test";

private static SchemaDescriptor TABLE_SCHEMA = new SchemaDescriptor(
1,
new Column[]{new Column("id".toUpperCase(), NativeTypes.INT32, false)},
new Column[]{
new Column("name".toUpperCase(), NativeTypes.STRING, true),
new Column("salary".toUpperCase(), NativeTypes.DOUBLE, true)
}
);

protected TableViewInternal testTable;

protected final TestInfo testInfo;

//TODO fsync can be turned on again after https://issues.apache.org/jira/browse/IGNITE-20195
@InjectConfiguration("mock: { fsync: false }")
protected static RaftConfiguration raftConfiguration;

@InjectConfiguration
protected static GcConfiguration gcConfig;

@InjectConfiguration
protected static TransactionConfiguration txConfiguration;

private ItTxTestCluster txTestCluster;

private HybridTimestampTracker timestampTracker = new HybridTimestampTracker();

/**
* The constructor.
*
* @param testInfo Test info.
*/
public ItLockTableTest(TestInfo testInfo) {
this.testInfo = testInfo;
}

@BeforeEach
public void before() throws Exception {
txTestCluster = new ItTxTestCluster(
testInfo,
raftConfiguration,
txConfiguration,
workDir,
1,
1,
false,
timestampTracker
) {
@Override
protected TxManagerImpl newTxManager(
ClusterService clusterService,
ReplicaService replicaSvc,
HybridClock clock,
TransactionIdGenerator generator,
ClusterNode node,
PlacementDriver placementDriver
) {
return new TxManagerImpl(
txConfiguration,
clusterService,
replicaSvc,
new HeapLockManager(
DeadlockPreventionPolicy.NO_OP,
HeapLockManager.SLOTS,
CACHE_SIZE,
new HeapUnboundedLockManager()),
clock,
generator,
placementDriver,
() -> DEFAULT_IDLE_SAFE_TIME_PROPAGATION_PERIOD_MILLISECONDS
);
}
};
txTestCluster.prepareCluster();

testTable = txTestCluster.startTable(TABLE_NAME, EMP_TABLE_ID, TABLE_SCHEMA);

log.info("Tables have been started");
}

@AfterEach
public void after() throws Exception {
txTestCluster.shutdownCluster();
}

/**
* Test that a lock table behaves correctly in case of lock cache overflow.
*/
@Test
public void testCollision() {
RecordView<Tuple> view = testTable.recordView();

int i = 0;
final int count = 1000;
List<Transaction> txns = new ArrayList<>();
while (i++ < count) {
Transaction tx = txTestCluster.igniteTransactions().begin();
view.insertAsync(tx, tuple(i, "x-" + i));
txns.add(tx);
}

assertTrue(TestUtils.waitForCondition(() -> {
int total = 0;
HeapLockManager lockManager = (HeapLockManager) txTestCluster.txManagers.get(txTestCluster.localNodeName).lockManager();
for (int j = 0; j < lockManager.getSlots().length; j++) {
LockState slot = lockManager.getSlots()[j];
total += slot.waitersCount();
}

return total == count && lockManager.available() == 0;
sanpwc marked this conversation as resolved.
Show resolved Hide resolved
}, 10_000), "Some lockers are missing");

int empty = 0;
int coll = 0;

HeapLockManager lm = (HeapLockManager) txTestCluster.txManagers.get(txTestCluster.localNodeName).lockManager();
for (int j = 0; j < lm.getSlots().length; j++) {
LockState slot = lm.getSlots()[j];
int cnt = slot.waitersCount();
if (cnt == 0) {
empty++;
}
if (cnt > 1) {
coll += cnt;
}
}

LOG.info("LockTable [emptySlots={} collisions={}]", empty, coll);

assertTrue(coll > 0);
sanpwc marked this conversation as resolved.
Show resolved Hide resolved

List<CompletableFuture<?>> finishFuts = new ArrayList<>();
for (Transaction txn : txns) {
finishFuts.add(txn.commitAsync());
}

for (CompletableFuture<?> finishFut : finishFuts) {
finishFut.join();
}

assertTrue(TestUtils.waitForCondition(() -> {
int total = 0;
HeapLockManager lockManager = (HeapLockManager) txTestCluster.txManagers.get(txTestCluster.localNodeName).lockManager();
for (int j = 0; j < lockManager.getSlots().length; j++) {
LockState slot = lockManager.getSlots()[j];
total += slot.waitersCount();
}

return total == 0 && lockManager.available() == CACHE_SIZE;
}, 10_000), "Illegal lock manager state");
}

private static Tuple tuple(int id, String name) {
return Tuple.create()
.set("id", id)
.set("name", name);
}

private static Tuple keyTuple(int id) {
return Tuple.create()
.set("id", id);
}
}
Expand Up @@ -75,10 +75,10 @@ public class ItTxDistributedTestSingleNode extends TxAbstractTest {

//TODO fsync can be turned on again after https://issues.apache.org/jira/browse/IGNITE-20195
@InjectConfiguration("mock: { fsync: false }")
protected static RaftConfiguration raftConfiguration;
protected RaftConfiguration raftConfiguration;

@InjectConfiguration
protected static TransactionConfiguration txConfiguration;
protected TransactionConfiguration txConfiguration;

/**
* Returns a count of nodes.
Expand Down Expand Up @@ -153,6 +153,7 @@ public void before() throws Exception {
@AfterEach
public void after() throws Exception {
txTestCluster.shutdownCluster();
Mockito.framework().clearInlineMocks();
sanpwc marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Expand Up @@ -253,6 +253,6 @@ public void testTwoReadWriteTransactionsWaitForCleanup() throws TransactionExcep
}

private static void releaseTxLocks(UUID txId, LockManager lockManager) {
lockManager.locks(txId).forEachRemaining(lockManager::release);
lockManager.releaseAll(txId);
}
}