Skip to content

Commit 19ddf1f

Browse files
committed
#80 refactor to mutable sync resource, handle rollback only
1 parent 0756551 commit 19ddf1f

File tree

4 files changed

+116
-95
lines changed

4 files changed

+116
-95
lines changed

src/main/java/com/arangodb/springframework/transaction/ArangoTransactionManager.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public ArangoTransactionManager(ArangoOperations operations, QueryTransactionBri
2929
setNestedTransactionAllowed(false);
3030
setTransactionSynchronization(SYNCHRONIZATION_ALWAYS);
3131
setValidateExistingTransaction(true);
32+
setGlobalRollbackOnParticipationFailure(true);
3233
setRollbackOnCommitFailure(true);
3334
}
3435

@@ -63,12 +64,9 @@ protected void doBegin(Object transaction, TransactionDefinition definition) thr
6364
ArangoTransactionObject tx = (ArangoTransactionObject) transaction;
6465
tx.configure(definition);
6566
DbName key = operations.getDatabaseName();
66-
rebind(key, tx.createResource());
67-
bridge.setCurrentTransaction(collections -> {
68-
ArangoTransactionResource resource = tx.getOrBegin(collections);
69-
rebind(key, resource);
70-
return resource.getStreamTransactionId();
71-
});
67+
TransactionSynchronizationManager.unbindResourceIfPossible(key);
68+
TransactionSynchronizationManager.bindResource(key, tx.getResource());
69+
bridge.setCurrentTransaction(collections -> tx.getOrBegin(collections).getStreamTransactionId());
7270
}
7371

7472
/**
@@ -111,12 +109,13 @@ protected boolean isExistingTransaction(Object transaction) throws TransactionEx
111109
}
112110

113111
@Override
114-
protected void doCleanupAfterCompletion(Object transaction) {
115-
TransactionSynchronizationManager.unbindResource(operations.getDatabaseName());
112+
protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
113+
ArangoTransactionObject tx = (ArangoTransactionObject) status.getTransaction();
114+
tx.setRollbackOnly();
116115
}
117116

118-
private static void rebind(DbName key, ArangoTransactionResource resource) {
119-
TransactionSynchronizationManager.unbindResourceIfPossible(key);
120-
TransactionSynchronizationManager.bindResource(key, resource);
117+
@Override
118+
protected void doCleanupAfterCompletion(Object transaction) {
119+
TransactionSynchronizationManager.unbindResourceIfPossible(operations.getDatabaseName());
121120
}
122121
}
Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.arangodb.springframework.transaction;
22

33
import com.arangodb.ArangoDatabase;
4-
import com.arangodb.DbName;
54
import com.arangodb.entity.StreamTransactionEntity;
65
import com.arangodb.entity.StreamTransactionStatus;
76
import com.arangodb.model.StreamTransactionOptions;
@@ -12,37 +11,37 @@
1211
import org.springframework.transaction.TransactionDefinition;
1312
import org.springframework.transaction.interceptor.TransactionAttribute;
1413
import org.springframework.transaction.support.SmartTransactionObject;
14+
import org.springframework.transaction.support.TransactionSynchronizationUtils;
1515

1616
import java.util.Collection;
17+
import java.util.Collections;
1718
import java.util.HashSet;
1819
import java.util.Set;
1920

21+
/**
22+
* Transaction object created by {@link ArangoTransactionManager}.
23+
*/
2024
class ArangoTransactionObject implements SmartTransactionObject {
2125

2226
private static final Log logger = LogFactory.getLog(ArangoTransactionObject.class);
2327

2428
private final ArangoDatabase database;
25-
private final Set<String> writeCollections = new HashSet<>();
29+
private final ArangoTransactionResource resource;
2630
private int timeout;
27-
private StreamTransactionEntity streamTransaction;
31+
private StreamTransactionEntity transaction;
2832

2933
ArangoTransactionObject(ArangoDatabase database, int defaultTimeout, @Nullable ArangoTransactionResource resource) {
3034
this.database = database;
35+
this.resource = resource == null ? new ArangoTransactionResource(null, Collections.emptySet(), false) : resource;
3136
this.timeout = defaultTimeout;
32-
if (resource != null) {
33-
writeCollections.addAll(resource.getCollectionNames());
34-
if (resource.getStreamTransactionId() != null) {
35-
streamTransaction = database.getStreamTransaction(resource.getStreamTransactionId());
36-
}
37-
}
3837
}
3938

40-
ArangoTransactionResource createResource() {
41-
return new ArangoTransactionResource(streamTransaction == null ? null : streamTransaction.getId(), writeCollections);
39+
ArangoTransactionResource getResource() {
40+
return resource;
4241
}
4342

4443
boolean exists() {
45-
return streamTransaction != null;
44+
return resource.getStreamTransactionId() != null;
4645
}
4746

4847
void configure(TransactionDefinition definition) {
@@ -56,55 +55,70 @@ void configure(TransactionDefinition definition) {
5655

5756
ArangoTransactionResource getOrBegin(Collection<String> collections) {
5857
addCollections(collections);
59-
if (streamTransaction != null) {
60-
return createResource();
58+
if (resource.getStreamTransactionId() != null) {
59+
return getResource();
6160
}
6261
StreamTransactionOptions options = new StreamTransactionOptions()
6362
.allowImplicit(true)
64-
.writeCollections(writeCollections.toArray(new String[0]))
63+
.writeCollections(resource.getCollectionNames().toArray(new String[0]))
6564
.lockTimeout(Math.max(timeout, 0));
66-
streamTransaction = database.beginStreamTransaction(options);
65+
transaction = database.beginStreamTransaction(options);
66+
resource.setStreamTransactionId(transaction.getId());
6767
if (logger.isDebugEnabled()) {
68-
logger.debug("Began stream transaction " + streamTransaction.getId() + " writing collections " + writeCollections);
68+
logger.debug("Began stream transaction " + resource.getStreamTransactionId() + " writing collections " + resource.getCollectionNames());
6969
}
70-
return createResource();
70+
return getResource();
7171
}
7272

7373
void commit() {
74-
if (streamTransaction != null && streamTransaction.getStatus() == StreamTransactionStatus.running) {
75-
database.commitStreamTransaction(streamTransaction.getId());
74+
if (isStatus(StreamTransactionStatus.running)) {
75+
database.commitStreamTransaction(resource.getStreamTransactionId());
7676
}
7777
}
7878

7979
void rollback() {
80-
if (streamTransaction != null && streamTransaction.getStatus() == StreamTransactionStatus.running) {
81-
database.abortStreamTransaction(streamTransaction.getId());
80+
if (isStatus(StreamTransactionStatus.running)) {
81+
database.abortStreamTransaction(resource.getStreamTransactionId());
8282
}
83+
setRollbackOnly();
8384
}
8485

8586
@Override
8687
public boolean isRollbackOnly() {
87-
return streamTransaction != null && streamTransaction.getStatus() == StreamTransactionStatus.aborted;
88+
return resource.isRollbackOnly() || isStatus(StreamTransactionStatus.aborted);
89+
}
90+
91+
public void setRollbackOnly() {
92+
resource.setRollbackOnly(true);
8893
}
8994

9095
@Override
9196
public void flush() {
92-
// nothing to do
97+
TransactionSynchronizationUtils.triggerFlush();
9398
}
9499

95100
@Override
96101
public String toString() {
97-
return streamTransaction == null ? "(not begun)" : streamTransaction.getId();
102+
return resource.getStreamTransactionId() == null ? "(not begun)" : resource.getStreamTransactionId();
98103
}
99104

100105
private void addCollections(Collection<String> collections) {
101-
if (streamTransaction != null) {
102-
if (!writeCollections.containsAll(collections)) {
106+
if (resource.getStreamTransactionId() != null) {
107+
if (!resource.getCollectionNames().containsAll(collections)) {
103108
Set<String> additional = new HashSet<>(collections);
104-
additional.removeAll(writeCollections);
105-
throw new IllegalTransactionStateException("Stream transaction already started on collections " + writeCollections + ", no additional collections allowed: " + additional);
109+
additional.removeAll(resource.getCollectionNames());
110+
throw new IllegalTransactionStateException("Stream transaction already started on collections " + resource.getCollectionNames() + ", no additional collections allowed: " + additional);
106111
}
107112
}
108-
writeCollections.addAll(collections);
113+
HashSet<String> all = new HashSet<>(resource.getCollectionNames());
114+
all.addAll(collections);
115+
resource.setCollectionNames(all);
116+
}
117+
118+
private boolean isStatus(StreamTransactionStatus status) {
119+
if (transaction == null && resource.getStreamTransactionId() != null) {
120+
transaction = database.getStreamTransaction(resource.getStreamTransactionId());
121+
}
122+
return transaction != null && transaction.getStatus() == status;
109123
}
110124
}
Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,51 @@
11
package com.arangodb.springframework.transaction;
22

33
import org.springframework.lang.Nullable;
4+
import org.springframework.transaction.support.TransactionSynchronizationManager;
45

5-
import java.util.Collection;
66
import java.util.HashSet;
7-
import java.util.Objects;
87
import java.util.Set;
98

9+
/**
10+
* Synchronisation resource (has to be mutable).
11+
*
12+
* @see TransactionSynchronizationManager#bindResource(Object, Object)
13+
* @see ArangoTransactionObject
14+
*/
1015
class ArangoTransactionResource {
1116

12-
private final String streamTransactionId;
13-
private final Set<String> collectionNames;
17+
private String streamTransactionId;
18+
private Set<String> collectionNames;
1419

15-
ArangoTransactionResource(@Nullable String streamTransactionId, Collection<String> collectionNames) {
20+
private boolean rollbackOnly;
21+
22+
ArangoTransactionResource(@Nullable String streamTransactionId, Set<String> collectionNames, boolean rollbackOnly) {
1623
this.streamTransactionId = streamTransactionId;
17-
this.collectionNames = new HashSet<>(collectionNames);
24+
setCollectionNames(collectionNames);
25+
this.rollbackOnly = rollbackOnly;
1826
}
1927

2028
String getStreamTransactionId() {
2129
return streamTransactionId;
2230
}
2331

32+
void setStreamTransactionId(String streamTransactionId) {
33+
this.streamTransactionId = streamTransactionId;
34+
}
35+
2436
Set<String> getCollectionNames() {
2537
return collectionNames;
2638
}
2739

28-
@Override
29-
public boolean equals(Object o) {
30-
if (this == o) return true;
31-
if (o == null || getClass() != o.getClass()) return false;
32-
ArangoTransactionResource that = (ArangoTransactionResource) o;
33-
return Objects.equals(streamTransactionId, that.streamTransactionId) && collectionNames.equals(that.collectionNames);
40+
void setCollectionNames(Set<String> collectionNames) {
41+
this.collectionNames = new HashSet<>(collectionNames);
42+
}
43+
44+
boolean isRollbackOnly() {
45+
return rollbackOnly;
3446
}
3547

36-
@Override
37-
public int hashCode() {
38-
return Objects.hash(streamTransactionId);
48+
void setRollbackOnly(boolean rollbackOnly) {
49+
this.rollbackOnly = rollbackOnly;
3950
}
4051
}

0 commit comments

Comments
 (0)