Skip to content

Commit 007c3b9

Browse files
Merge pull request #2 from grcevski/spacetime_transactions
WIP: Transaction ops in translog
2 parents 98f9902 + a781eb6 commit 007c3b9

File tree

19 files changed

+716
-126
lines changed

19 files changed

+716
-126
lines changed

server/src/internalClusterTest/java/org/elasticsearch/index/shard/IndexShardIT.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,8 @@ public void testMaybeFlush() throws Exception {
362362
SequenceNumbers.UNASSIGNED_SEQ_NO,
363363
0,
364364
IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP,
365-
false
365+
false,
366+
IndexShard.NO_TRANSACTION_ID
366367
);
367368
assertTrue(shard.shouldPeriodicallyFlush());
368369
final Translog translog = getTranslog(shard);
@@ -447,7 +448,8 @@ public void testMaybeRollTranslogGeneration() throws Exception {
447448
SequenceNumbers.UNASSIGNED_SEQ_NO,
448449
0,
449450
IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP,
450-
false
451+
false,
452+
IndexShard.NO_TRANSACTION_ID
451453
);
452454
final Translog.Location location = result.getTranslogLocation();
453455
shard.afterWriteOperation();

server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
3434
import org.elasticsearch.cluster.action.shard.ShardStateAction;
3535
import org.elasticsearch.cluster.service.ClusterService;
36+
import org.elasticsearch.common.UUIDs;
3637
import org.elasticsearch.common.bytes.BytesReference;
3738
import org.elasticsearch.common.compress.CompressedXContent;
3839
import org.elasticsearch.common.inject.Inject;
@@ -181,20 +182,33 @@ public static void performOnPrimary(
181182

182183
@Override
183184
protected void doRun() throws Exception {
184-
while (context.hasMoreOperationsToExecute()) {
185-
if (executeBulkItemRequest(
186-
context,
187-
updateHelper,
188-
nowInMillisSupplier,
189-
mappingUpdater,
190-
waitForMappingUpdate,
191-
ActionListener.wrap(v -> executor.execute(this), this::onRejection)
192-
) == false) {
193-
// We are waiting for a mapping update on another thread, that will invoke this action again once its done
194-
// so we just break out here.
195-
return;
185+
String uid = UUIDs.base64UUID();
186+
long transactionId = -1L;
187+
try {
188+
transactionId = primary.startTransaction(uid);
189+
while (context.hasMoreOperationsToExecute()) {
190+
if (executeBulkItemRequest(
191+
context,
192+
updateHelper,
193+
nowInMillisSupplier,
194+
mappingUpdater,
195+
waitForMappingUpdate,
196+
ActionListener.wrap(v -> executor.execute(this), this::onRejection),
197+
transactionId
198+
) == false) {
199+
// We are waiting for a mapping update on another thread, that will invoke this action again once its done
200+
// so we just break out here.
201+
return;
202+
}
203+
assert context.isInitial(); // either completed and moved to next or reset
196204
}
197-
assert context.isInitial(); // either completed and moved to next or reset
205+
206+
primary.commitTransaction(uid, transactionId);
207+
} catch (Exception x) {
208+
logger.warn("Encountered an error while executing bulk transaction", x);
209+
primary.rollbackTransaction(uid, transactionId);
210+
} finally {
211+
primary.closeTransaction(uid, transactionId);
198212
}
199213
primary.getBulkOperationListener().afterBulk(request.totalSizeInBytes(), System.nanoTime() - startBulkTime);
200214
// We're done, there's no more operations to execute so we resolve the wrapped listener
@@ -206,7 +220,6 @@ public void onRejection(Exception e) {
206220
// We must finish the outstanding request. Finishing the outstanding request can include
207221
// refreshing and fsyncing. Therefore, we must force execution on the WRITE thread.
208222
executor.execute(new ActionRunnable<>(listener) {
209-
210223
@Override
211224
protected void doRun() {
212225
// Fail all operations after a bulk rejection hit an action that waited for a mapping update and finish the request
@@ -250,6 +263,25 @@ private void finishRequest() {
250263
}.run();
251264
}
252265

266+
static boolean executeBulkItemRequest(
267+
BulkPrimaryExecutionContext context,
268+
UpdateHelper updateHelper,
269+
LongSupplier nowInMillisSupplier,
270+
MappingUpdatePerformer mappingUpdater,
271+
Consumer<ActionListener<Void>> waitForMappingUpdate,
272+
ActionListener<Void> itemDoneListener
273+
) throws Exception {
274+
return executeBulkItemRequest(
275+
context,
276+
updateHelper,
277+
nowInMillisSupplier,
278+
mappingUpdater,
279+
waitForMappingUpdate,
280+
itemDoneListener,
281+
IndexShard.NO_TRANSACTION_ID
282+
);
283+
}
284+
253285
/**
254286
* Executes bulk item requests and handles request execution exceptions.
255287
* @return {@code true} if request completed on this thread and the listener was invoked, {@code false} if the request triggered
@@ -261,7 +293,8 @@ static boolean executeBulkItemRequest(
261293
LongSupplier nowInMillisSupplier,
262294
MappingUpdatePerformer mappingUpdater,
263295
Consumer<ActionListener<Void>> waitForMappingUpdate,
264-
ActionListener<Void> itemDoneListener
296+
ActionListener<Void> itemDoneListener,
297+
long transactionId
265298
) throws Exception {
266299
final DocWriteRequest.OpType opType = context.getCurrent().opType();
267300

@@ -306,7 +339,8 @@ static boolean executeBulkItemRequest(
306339
request.id(),
307340
request.versionType(),
308341
request.ifSeqNo(),
309-
request.ifPrimaryTerm()
342+
request.ifPrimaryTerm(),
343+
transactionId
310344
);
311345
} else {
312346
final IndexRequest request = context.getRequestToExecute();
@@ -324,7 +358,8 @@ static boolean executeBulkItemRequest(
324358
request.ifSeqNo(),
325359
request.ifPrimaryTerm(),
326360
request.getAutoGeneratedTimestamp(),
327-
request.isRetry()
361+
request.isRetry(),
362+
transactionId
328363
);
329364
}
330365
if (result.getResultType() == Engine.Result.Type.MAPPING_UPDATE_REQUIRED) {
@@ -509,7 +544,7 @@ private static BulkItemResponse processUpdateResponse(
509544
protected void dispatchedShardOperationOnReplica(BulkShardRequest request, IndexShard replica, ActionListener<ReplicaResult> listener) {
510545
ActionListener.completeWith(listener, () -> {
511546
final long startBulkTime = System.nanoTime();
512-
final Translog.Location location = performOnReplica(request, replica);
547+
final Translog.Location location = performOnReplica(request, replica, IndexShard.NO_TRANSACTION_ID);
513548
replica.getBulkOperationListener().afterBulk(request.totalSizeInBytes(), System.nanoTime() - startBulkTime);
514549
return new WriteReplicaResult<>(request, location, null, replica, logger);
515550
});
@@ -525,7 +560,7 @@ protected int replicaOperationCount(BulkShardRequest request) {
525560
return request.items().length;
526561
}
527562

528-
public static Translog.Location performOnReplica(BulkShardRequest request, IndexShard replica) throws Exception {
563+
public static Translog.Location performOnReplica(BulkShardRequest request, IndexShard replica, long transactionId) throws Exception {
529564
Translog.Location location = null;
530565
for (int i = 0; i < request.items().length; i++) {
531566
final BulkItemRequest item = request.items()[i];
@@ -553,7 +588,7 @@ public static Translog.Location performOnReplica(BulkShardRequest request, Index
553588
continue; // ignore replication as it's a noop
554589
}
555590
assert response.getResponse().getSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO;
556-
operationResult = performOpOnReplica(response.getResponse(), item.request(), replica);
591+
operationResult = performOpOnReplica(response.getResponse(), item.request(), replica, transactionId);
557592
}
558593
assert operationResult != null : "operation result must never be null when primary response has no failure";
559594
location = syncOperationResultOrThrow(operationResult, location);
@@ -564,7 +599,8 @@ public static Translog.Location performOnReplica(BulkShardRequest request, Index
564599
private static Engine.Result performOpOnReplica(
565600
DocWriteResponse primaryResponse,
566601
DocWriteRequest<?> docWriteRequest,
567-
IndexShard replica
602+
IndexShard replica,
603+
long transactionId
568604
) throws Exception {
569605
final Engine.Result result;
570606
switch (docWriteRequest.opType()) {
@@ -584,7 +620,8 @@ private static Engine.Result performOpOnReplica(
584620
primaryResponse.getVersion(),
585621
indexRequest.getAutoGeneratedTimestamp(),
586622
indexRequest.isRetry(),
587-
sourceToParse
623+
sourceToParse,
624+
transactionId
588625
);
589626
break;
590627
case DELETE:
@@ -593,7 +630,8 @@ private static Engine.Result performOpOnReplica(
593630
primaryResponse.getSeqNo(),
594631
primaryResponse.getPrimaryTerm(),
595632
primaryResponse.getVersion(),
596-
deleteRequest.id()
633+
deleteRequest.id(),
634+
transactionId
597635
);
598636
break;
599637
default:

server/src/main/java/org/elasticsearch/index/engine/Engine.java

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ public Condition newCondition() {
321321
}
322322
}
323323

324+
public abstract long startTransaction(String id) throws IOException;
325+
326+
public abstract boolean commitTransaction(String id, long transactionId) throws IOException;
327+
328+
public abstract boolean rollbackTransaction(String id, long transactionId) throws IOException;
329+
330+
public abstract boolean closeTransaction(String id, long transactionId) throws IOException;
331+
324332
/**
325333
* Perform document index operation on the engine
326334
* @param index operation to perform
@@ -1271,7 +1279,8 @@ public abstract static class Operation {
12711279
public enum TYPE {
12721280
INDEX,
12731281
DELETE,
1274-
NO_OP;
1282+
NO_OP,
1283+
TX_OP;
12751284

12761285
private final String lowercase;
12771286

@@ -1363,6 +1372,7 @@ public static class Index extends Operation {
13631372
private final boolean isRetry;
13641373
private final long ifSeqNo;
13651374
private final long ifPrimaryTerm;
1375+
private final long transactionId;
13661376

13671377
public Index(
13681378
Term uid,
@@ -1377,6 +1387,38 @@ public Index(
13771387
boolean isRetry,
13781388
long ifSeqNo,
13791389
long ifPrimaryTerm
1390+
) {
1391+
this(
1392+
uid,
1393+
doc,
1394+
seqNo,
1395+
primaryTerm,
1396+
version,
1397+
versionType,
1398+
origin,
1399+
startTime,
1400+
autoGeneratedIdTimestamp,
1401+
isRetry,
1402+
ifSeqNo,
1403+
ifPrimaryTerm,
1404+
-1L
1405+
);
1406+
}
1407+
1408+
public Index(
1409+
Term uid,
1410+
ParsedDocument doc,
1411+
long seqNo,
1412+
long primaryTerm,
1413+
long version,
1414+
VersionType versionType,
1415+
Origin origin,
1416+
long startTime,
1417+
long autoGeneratedIdTimestamp,
1418+
boolean isRetry,
1419+
long ifSeqNo,
1420+
long ifPrimaryTerm,
1421+
long transactionId
13801422
) {
13811423
super(uid, seqNo, primaryTerm, version, versionType, origin, startTime);
13821424
assert (origin == Origin.PRIMARY) == (versionType != null) : "invalid version_type=" + versionType + " for origin=" + origin;
@@ -1389,6 +1431,7 @@ public Index(
13891431
this.autoGeneratedIdTimestamp = autoGeneratedIdTimestamp;
13901432
this.ifSeqNo = ifSeqNo;
13911433
this.ifPrimaryTerm = ifPrimaryTerm;
1434+
this.transactionId = transactionId;
13921435
}
13931436

13941437
public Index(Term uid, long primaryTerm, ParsedDocument doc) {
@@ -1467,13 +1510,18 @@ public long getIfSeqNo() {
14671510
public long getIfPrimaryTerm() {
14681511
return ifPrimaryTerm;
14691512
}
1513+
1514+
public long getTransactionId() {
1515+
return transactionId;
1516+
}
14701517
}
14711518

14721519
public static class Delete extends Operation {
14731520

14741521
private final String id;
14751522
private final long ifSeqNo;
14761523
private final long ifPrimaryTerm;
1524+
private final long transactionId;
14771525

14781526
public Delete(
14791527
String id,
@@ -1486,6 +1534,22 @@ public Delete(
14861534
long startTime,
14871535
long ifSeqNo,
14881536
long ifPrimaryTerm
1537+
) {
1538+
this(id, uid, seqNo, primaryTerm, version, versionType, origin, startTime, ifSeqNo, ifPrimaryTerm, -1L);
1539+
}
1540+
1541+
public Delete(
1542+
String id,
1543+
Term uid,
1544+
long seqNo,
1545+
long primaryTerm,
1546+
long version,
1547+
VersionType versionType,
1548+
Origin origin,
1549+
long startTime,
1550+
long ifSeqNo,
1551+
long ifPrimaryTerm,
1552+
long transactionId
14891553
) {
14901554
super(uid, seqNo, primaryTerm, version, versionType, origin, startTime);
14911555
assert (origin == Origin.PRIMARY) == (versionType != null) : "invalid version_type=" + versionType + " for origin=" + origin;
@@ -1496,6 +1560,7 @@ public Delete(
14961560
this.id = Objects.requireNonNull(id);
14971561
this.ifSeqNo = ifSeqNo;
14981562
this.ifPrimaryTerm = ifPrimaryTerm;
1563+
this.transactionId = transactionId;
14991564
}
15001565

15011566
public Delete(String id, Term uid, long primaryTerm) {
@@ -1550,6 +1615,10 @@ public long getIfSeqNo() {
15501615
public long getIfPrimaryTerm() {
15511616
return ifPrimaryTerm;
15521617
}
1618+
1619+
public long getTransactionId() {
1620+
return transactionId;
1621+
}
15531622
}
15541623

15551624
public static class NoOp extends Operation {
@@ -1597,6 +1666,43 @@ public int estimatedSizeInBytes() {
15971666

15981667
}
15991668

1669+
public static class TxOp extends Operation {
1670+
public TxOp(final long startTime) {
1671+
super(null, UNASSIGNED_SEQ_NO, UNASSIGNED_PRIMARY_TERM, Versions.NOT_FOUND, null, null, startTime);
1672+
}
1673+
1674+
@Override
1675+
public Term uid() {
1676+
throw new UnsupportedOperationException();
1677+
}
1678+
1679+
@Override
1680+
public long version() {
1681+
throw new UnsupportedOperationException();
1682+
}
1683+
1684+
@Override
1685+
public VersionType versionType() {
1686+
throw new UnsupportedOperationException();
1687+
}
1688+
1689+
@Override
1690+
String id() {
1691+
throw new UnsupportedOperationException();
1692+
}
1693+
1694+
@Override
1695+
public TYPE operationType() {
1696+
return TYPE.TX_OP;
1697+
}
1698+
1699+
@Override
1700+
public int estimatedSizeInBytes() {
1701+
return 2 * Long.BYTES;
1702+
}
1703+
1704+
}
1705+
16001706
public static class Get {
16011707
private final boolean realtime;
16021708
private final Term uid;

0 commit comments

Comments
 (0)