Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/_docs/SQL/JDBC/jdbc-driver.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ JDBC Thin Driver supports the standard JDBC savepoint API for explicit transacti
* `Connection.rollback(Savepoint savepoint)`
* `Connection.releaseSavepoint(Savepoint savepoint)`

Savepoints are available for JDBC connections that use the Calcite-based SQL engine and explicit `PESSIMISTIC` transactions.
Savepoints are available for JDBC connections that use the Calcite-based SQL engine and explicit transactions.
Disable auto-commit before creating a savepoint.
If auto-commit is left enabled, JDBC savepoint API calls requiring an explicit transaction will fail with 'SQLException'.

Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/key-value-api/transactions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ It is critical that an Ignite Transaction should be `closed` regardless of its c
Savepoints allow you to mark an intermediate state inside an explicit transaction and later roll back only the changes made after that point.
They are useful when a transaction contains several logical steps and one of the later steps can be discarded without rolling back the whole transaction.

Ignite supports savepoints only for explicit `PESSIMISTIC` transactions.
Ignite supports savepoints only for explicit transactions.
Savepoints are local to the transaction that created them and are removed when the transaction is committed or rolled back.

Use `Transaction.savepoint(name)` to create a savepoint.
Expand Down
4 changes: 2 additions & 2 deletions docs/_docs/sql-reference/transactions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ SAVEPOINT savepointName

=== Description

`SAVEPOINT` can be used only inside an explicit `PESSIMISTIC` transaction.
`SAVEPOINT` can be used only inside an explicit transaction.

The command records the current transaction state. Later, you can use `ROLLBACK TO SAVEPOINT` to roll back all transaction changes made after the savepoint was created.

Expand All @@ -52,7 +52,7 @@ ROLLBACK TO SAVEPOINT savepointName

=== Description

`ROLLBACK TO SAVEPOINT` can be used only inside an explicit `PESSIMISTIC` transaction.
`ROLLBACK TO SAVEPOINT` can be used only inside an explicit transaction.

The command rolls back all transaction changes made after the specified savepoint was created. The transaction remains active and can be committed or rolled back later.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,36 @@
import org.apache.ignite.configuration.SqlConfiguration;
import org.apache.ignite.configuration.TransactionConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import static org.apache.ignite.testframework.GridTestUtils.assertThrows;

/** Savepoint tests for thin JDBC connection. */
@RunWith(Parameterized.class)
public class JdbcThinConnectionSavepointTest extends AbstractJdbcTest {
/** */
private static final String TBL = "SAVEPOINT_TEST_TABLE";

/** JDBC URL. */
private static final String SAVEPOINT_URL = URL + "?queryEngine=" + CalciteQueryEngineConfiguration.ENGINE_NAME +
"&transactionConcurrency=PESSIMISTIC";
private static final String SAVEPOINT_URL = URL + "?queryEngine=" + CalciteQueryEngineConfiguration.ENGINE_NAME;

/** Transaction concurrency URL parameter. */
@Parameter
public String txConcurrencyParam;

/**
* @return Test parameters.
*/
@Parameters(name = "{0}")
public static Iterable<Object[]> testData() {
return Arrays.asList(new Object[][] {
{"&transactionConcurrency=PESSIMISTIC"},
{"&transactionConcurrency=OPTIMISTIC"}
});
}

/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
Expand Down Expand Up @@ -242,7 +261,7 @@ public void testSqlDmlChangesCanBeRolledBackToSavepointUsingJdbc() throws Except
* @return Connection.
*/
private Connection connection() throws SQLException {
return DriverManager.getConnection(SAVEPOINT_URL);
return DriverManager.getConnection(SAVEPOINT_URL + txConcurrencyParam);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.apache.ignite.internal.processors.tx;

import java.util.Arrays;
import java.util.List;
import org.apache.ignite.cache.query.FieldsQueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
Expand All @@ -30,19 +31,39 @@
import org.apache.ignite.internal.processors.query.calcite.integration.AbstractBasicIntegrationTest;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal.SAVEPOINTS_EXPLICIT_TX_ONLY;
import static org.apache.ignite.internal.processors.query.calcite.integration.AbstractBasicIntegrationTransactionalTest.SqlTransactionMode.ALL;
import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED;

/** Tests SQL savepoint commands executed by Calcite. */
@RunWith(Parameterized.class)
public class SqlTransactionsSavepointTest extends AbstractBasicIntegrationTest {
/** */
private static final String TBL = "SAVEPOINT_TEST_TABLE";

/** */
@Parameter
public TransactionConcurrency txConcurrency;

/** */
@Parameters(name = "{0}")
public static Iterable<Object[]> testData() {
return Arrays.asList(new Object[][] {
{PESSIMISTIC},
{OPTIMISTIC}
});
}

/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
return super.getConfiguration(igniteInstanceName)
Expand All @@ -69,7 +90,7 @@ public class SqlTransactionsSavepointTest extends AbstractBasicIntegrationTest {
/** */
@Test
public void testSavepointCommandsInSqlScript() {
try (Transaction tx = client.transactions().txStart(PESSIMISTIC, READ_COMMITTED)) {
try (Transaction tx = client.transactions().txStart(txConcurrency, READ_COMMITTED)) {
sqlScript(
"INSERT INTO " + TBL + " VALUES (1, 'before_sp1');" +
"SAVEPOINT sp1;" +
Expand Down Expand Up @@ -110,7 +131,7 @@ public void testSavepointCommandsInSqlScript() {
/** */
@Test
public void testSqlDmlChangesCanBeRolledBackToSavepointUsingSqlCommands() {
try (Transaction tx = client.transactions().txStart(PESSIMISTIC, READ_COMMITTED)) {
try (Transaction tx = client.transactions().txStart(txConcurrency, READ_COMMITTED)) {
sql("INSERT INTO " + TBL + " VALUES (1, 'before_sp1')");

sql("SAVEPOINT sp1");
Expand Down Expand Up @@ -154,7 +175,7 @@ public void testSqlDmlChangesCanBeRolledBackToSavepointUsingSqlCommands() {
/** */
@Test
public void testSqlDmlChangesCanBeRolledBackToSavepoint() {
try (Transaction tx = client.transactions().txStart(PESSIMISTIC, READ_COMMITTED)) {
try (Transaction tx = client.transactions().txStart(txConcurrency, READ_COMMITTED)) {
sql("INSERT INTO " + TBL + " VALUES (1, 'before_sp1')");

tx.savepoint("sp1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3034,7 +3034,7 @@ private void resume(long threadId) throws IgniteCheckedException {
}

/**
* Creates savepoint for a pessimistic transaction.
* Creates savepoint for a transaction.
*
* @param name Savepoint name.
* @param overwrite Whether to overwrite an existing savepoint with the same name.
Expand All @@ -3050,9 +3050,6 @@ public void savepoint(String name, boolean overwrite) throws IgniteCheckedExcept
if (implicit())
throw new IgniteCheckedException(SAVEPOINTS_EXPLICIT_TX_ONLY);

if (!pessimistic())
throw new IgniteCheckedException("Savepoints are supported only for PESSIMISTIC transactions.");

ListIterator<TxSavepoint> spIter = findSavepoint(name);

if (spIter != null) {
Expand Down Expand Up @@ -3087,9 +3084,6 @@ public void rollbackToSavepoint(String name) throws IgniteCheckedException {
if (implicit())
throw new IgniteCheckedException(SAVEPOINTS_EXPLICIT_TX_ONLY);

if (!pessimistic())
throw new IgniteCheckedException("Savepoints are supported only for PESSIMISTIC transactions.");

ListIterator<TxSavepoint> spIter = findSavepoint(name);

if (spIter == null)
Expand Down Expand Up @@ -3202,7 +3196,8 @@ private void rollbackToSavepoint(TxSavepoint savepoint) {
entriesToUnlock.add(curEntry);
}

unlockTxEntries(entriesToUnlock);
if (pessimistic())
unlockTxEntries(entriesToUnlock);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,8 @@ public interface Transaction extends AutoCloseable, IgniteAsyncSupport {
/**
* Creates a savepoint in the current transaction.
* <p>
* Savepoints are supported only for explicit transactions with
* {@link TransactionConcurrency#PESSIMISTIC} concurrency. The savepoint keeps the current transaction state and can
* later be used by {@link #rollbackToSavepoint(String)} to discard changes made after it was created.
* Savepoints are supported only for explicit transactions. The savepoint keeps the current transaction state and
* can later be used by {@link #rollbackToSavepoint(String)} to discard changes made after it was created.
*
* @param name Savepoint name.
* @throws TransactionException If savepoint with the same name already exists.
Expand All @@ -308,9 +307,8 @@ public interface Transaction extends AutoCloseable, IgniteAsyncSupport {
/**
* Creates a savepoint in the current transaction.
* <p>
* Savepoints are supported only for explicit transactions with
* {@link TransactionConcurrency#PESSIMISTIC} concurrency. If {@code overwrite} is {@code true} and a savepoint with
* the same name exists, the existing savepoint is replaced with a snapshot of the current transaction state.
* Savepoints are supported only for explicit transactions. If {@code overwrite} is {@code true} and a savepoint
* with the same name exists, the existing savepoint is replaced with a snapshot of the current transaction state.
*
* @param name Savepoint name.
* @param overwrite Whether to overwrite an existing savepoint with the same name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED;
import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;

/**
* Tests for transaction savepoint API.
Expand Down Expand Up @@ -173,6 +175,43 @@ public static Collection<Object[]> testData() {
{true, false, 0, true, true, REPEATABLE_READ},
{false, true, 0, true, true, REPEATABLE_READ},
{false, false, 0, true, true, REPEATABLE_READ},

// SERIALIZABLE
// backups = 0
{true, true, 0, true, false, SERIALIZABLE},
{true, true, 0, false, false, SERIALIZABLE},
{true, false, 0, true, false, SERIALIZABLE},
{true, false, 0, false, false, SERIALIZABLE},
{false, true, 0, true, false, SERIALIZABLE},
{false, true, 0, false, false, SERIALIZABLE},
{false, false, 0, true, false, SERIALIZABLE},
{false, false, 0, false, false, SERIALIZABLE},

// backups = 1
{true, true, 1, true, false, SERIALIZABLE},
{true, true, 1, false, false, SERIALIZABLE},
{true, false, 1, true, false, SERIALIZABLE},
{true, false, 1, false, false, SERIALIZABLE},
{false, true, 1, true, false, SERIALIZABLE},
{false, true, 1, false, false, SERIALIZABLE},
{false, false, 1, true, false, SERIALIZABLE},
{false, false, 1, false, false, SERIALIZABLE},

// backups = 2
{true, true, 2, true, false, SERIALIZABLE},
{true, true, 2, false, false, SERIALIZABLE},
{true, false, 2, true, false, SERIALIZABLE},
{true, false, 2, false, false, SERIALIZABLE},
{false, true, 2, true, false, SERIALIZABLE},
{false, true, 2, false, false, SERIALIZABLE},
{false, false, 2, true, false, SERIALIZABLE},
{false, false, 2, false, false, SERIALIZABLE},

// replicated cache.
{true, true, 0, true, true, SERIALIZABLE},
{true, false, 0, true, true, SERIALIZABLE},
{false, true, 0, true, true, SERIALIZABLE},
{false, false, 0, true, true, SERIALIZABLE},
});
}

Expand Down Expand Up @@ -402,6 +441,48 @@ public void testRollbackToSavepoint() {
assertEquals(Integer.valueOf(3), cache0.get(key3));
}

/**
* @throws Exception If failed.
*/
@Test
public void testRollbackToSavepointForOptimisticTransaction() throws Exception {
IgniteCache<Integer, Integer> cache0 = transactionalCache(ignite0);

int key1 = keyForPrimaryAndBackup(ignite0, ignite1);
int key2 = keyForPrimaryAndBackup(ignite0, ignite2);
int key3 = keyForPrimaryAndBackup(ignite0, ignite3);

if (initKeies) {
cache0.put(key1, -1);
cache0.put(key2, -1);
cache0.put(key3, -1);
}

try (Transaction tx = ignite0.transactions().txStart(OPTIMISTIC, transactionIsolation, 30_000, 3)) {
cache0.put(key1, 1);

tx.savepoint("sp");

cache0.put(key1, 10);
cache0.put(key2, 2);

tx.rollbackToSavepoint("sp");

cache0.put(key3, 3);

tx.commit();
}

assertEquals(Integer.valueOf(1), cache0.get(key1));

if (initKeies)
assertEquals(Integer.valueOf(-1), cache0.get(key2));
else
assertNull(cache0.get(key2));

assertEquals(Integer.valueOf(3), cache0.get(key3));
}

/**
* @throws Exception If failed.
*/
Expand Down
Loading
Loading