|
17 | 17 | import org.slf4j.LoggerFactory; |
18 | 18 |
|
19 | 19 | import tech.ydb.common.transaction.TxMode; |
| 20 | +import tech.ydb.common.transaction.VirtualTimestamp; |
20 | 21 | import tech.ydb.common.transaction.impl.YdbTransactionImpl; |
21 | 22 | import tech.ydb.core.Issue; |
22 | 23 | import tech.ydb.core.Result; |
|
29 | 30 | import tech.ydb.core.tracing.Scope; |
30 | 31 | import tech.ydb.core.tracing.Span; |
31 | 32 | import tech.ydb.core.utils.URITools; |
32 | | -import tech.ydb.core.utils.UpdatableOptional; |
33 | 33 | import tech.ydb.proto.ValueProtos; |
| 34 | +import tech.ydb.proto.common.CommonProtos; |
34 | 35 | import tech.ydb.proto.formats.YdbFormats; |
35 | 36 | import tech.ydb.proto.query.YdbQuery; |
36 | 37 | import tech.ydb.query.QuerySession; |
@@ -74,14 +75,12 @@ abstract class SessionImpl implements QuerySession { |
74 | 75 | private final QueryServiceRpc rpc; |
75 | 76 | private final String sessionId; |
76 | 77 | private final long nodeID; |
77 | | - private final boolean isTraceEnabled; |
78 | 78 | private final AtomicReference<TransactionImpl> transaction; |
79 | 79 |
|
80 | 80 | SessionImpl(QueryServiceRpc rpc, YdbQuery.CreateSessionResponse response) { |
81 | 81 | this.rpc = rpc; |
82 | 82 | this.sessionId = response.getSessionId(); |
83 | 83 | this.nodeID = getNodeBySessionId(response.getSessionId(), response.getNodeId()); |
84 | | - this.isTraceEnabled = logger.isTraceEnabled(); |
85 | 84 | this.transaction = new AtomicReference<>(new TransactionImpl(TxMode.SERIALIZABLE_RW, null)); |
86 | 85 | } |
87 | 86 |
|
@@ -393,68 +392,103 @@ abstract class StreamImpl implements QueryStream { |
393 | 392 |
|
394 | 393 | abstract void handleTxMeta(String txId); |
395 | 394 |
|
396 | | - void handleCompletion(Status status, Throwable th) { |
397 | | - } |
| 395 | + void handleCompletion(Status status, Throwable th) { } |
398 | 396 |
|
399 | 397 | @Override |
400 | 398 | public CompletableFuture<Result<QueryInfo>> execute(PartsHandler handler) { |
401 | | - final UpdatableOptional<Status> operationStatus = new UpdatableOptional<>(); |
402 | | - final UpdatableOptional<QueryStats> stats = new UpdatableOptional<>(); |
403 | | - return Span.endOnResult(span, grpcStream.start(msg -> { |
404 | | - if (isTraceEnabled) { |
405 | | - logger.trace("{} got stream message {}", |
406 | | - SessionImpl.this, TextFormat.shortDebugString(msg)); |
407 | | - } |
408 | | - Issue[] issues = Issue.fromPb(msg.getIssuesList()); |
409 | | - Status status = Status.of(StatusCode.fromProto(msg.getStatus()), issues); |
410 | | - |
411 | | - updateSessionState(status); |
412 | | - |
413 | | - if (!status.isSuccess()) { |
414 | | - handleTxMeta(null); |
415 | | - operationStatus.update(status); |
416 | | - return; |
417 | | - } |
418 | | - |
419 | | - if (msg.hasTxMeta()) { |
420 | | - handleTxMeta(msg.getTxMeta().getId()); |
421 | | - } |
422 | | - if (issues.length > 0) { |
423 | | - if (handler != null) { |
424 | | - handler.onIssues(issues); |
425 | | - } else { |
426 | | - logger.trace("{} lost issues message", SessionImpl.this); |
427 | | - } |
428 | | - } |
429 | | - if (msg.hasExecStats()) { |
430 | | - stats.update(new QueryStats(msg.getExecStats())); |
431 | | - } |
432 | | - |
433 | | - if (msg.hasResultSet()) { |
434 | | - long index = msg.getResultSetIndex(); |
435 | | - if (handler != null) { |
436 | | - handler.onNextRawPart(index, msg.getResultSet()); |
437 | | - } else { |
438 | | - logger.trace("{} lost result set part with index {}", SessionImpl.this, index); |
439 | | - } |
440 | | - } |
441 | | - }).whenComplete(this::handleCompletion).thenApply(streamStatus -> { |
| 399 | + Observer observer = new Observer(handler); |
| 400 | + CompletableFuture<Result<QueryInfo>> result = grpcStream.start(observer) |
| 401 | + .whenComplete(this::handleCompletion) |
| 402 | + .thenApply(streamStatus -> { |
442 | 403 | updateSessionState(streamStatus); |
443 | | - Status status = operationStatus.orElse(streamStatus); |
| 404 | + Status status = observer.mergedStatus(streamStatus); |
444 | 405 | if (status.isSuccess()) { |
445 | | - return Result.success(new QueryInfo(stats.get()), streamStatus); |
| 406 | + return Result.success(observer.buildQueryInfo(), status); |
446 | 407 | } else { |
447 | 408 | return Result.fail(status); |
448 | 409 | } |
449 | | - }) |
450 | | - ); |
| 410 | + }); |
| 411 | + |
| 412 | + return Span.endOnResult(span, result); |
451 | 413 | } |
452 | 414 |
|
453 | 415 | @Override |
454 | 416 | public void cancel() { |
455 | 417 | updateSessionState(CANCELLED); |
456 | 418 | grpcStream.cancel(); |
457 | 419 | } |
| 420 | + |
| 421 | + private class Observer implements GrpcReadStream.Observer<YdbQuery.ExecuteQueryResponsePart> { |
| 422 | + private final PartsHandler handler; |
| 423 | + |
| 424 | + private volatile Status queryStatus = null; |
| 425 | + private volatile QueryStats stats = null; |
| 426 | + private volatile VirtualTimestamp commitVt = null; |
| 427 | + private volatile VirtualTimestamp snapshotVt = null; |
| 428 | + |
| 429 | + Observer(PartsHandler handler) { |
| 430 | + this.handler = handler; |
| 431 | + } |
| 432 | + |
| 433 | + public Status mergedStatus(Status streamStatus) { |
| 434 | + return (streamStatus.isSuccess() && queryStatus != null) ? queryStatus : streamStatus; |
| 435 | + } |
| 436 | + |
| 437 | + public QueryInfo buildQueryInfo() { |
| 438 | + return new QueryInfo(stats, commitVt, snapshotVt); |
| 439 | + } |
| 440 | + |
| 441 | + @Override |
| 442 | + public void onNext(YdbQuery.ExecuteQueryResponsePart msg) { |
| 443 | + if (logger.isTraceEnabled()) { |
| 444 | + logger.trace("{} got stream message {}", SessionImpl.this, TextFormat.shortDebugString(msg)); |
| 445 | + } |
| 446 | + |
| 447 | + Issue[] issues = Issue.fromPb(msg.getIssuesList()); |
| 448 | + Status status = Status.of(StatusCode.fromProto(msg.getStatus()), issues); |
| 449 | + |
| 450 | + updateSessionState(status); |
| 451 | + |
| 452 | + if (!status.isSuccess()) { |
| 453 | + handleTxMeta(null); |
| 454 | + queryStatus = status; |
| 455 | + return; |
| 456 | + } |
| 457 | + |
| 458 | + if (msg.hasTxMeta()) { |
| 459 | + handleTxMeta(msg.getTxMeta().getId()); |
| 460 | + } |
| 461 | + if (issues.length > 0) { |
| 462 | + if (handler != null) { |
| 463 | + handler.onIssues(issues); |
| 464 | + } else { |
| 465 | + logger.trace("{} lost issues message", SessionImpl.this); |
| 466 | + } |
| 467 | + } |
| 468 | + if (msg.hasExecStats()) { |
| 469 | + stats = new QueryStats(msg.getExecStats()); |
| 470 | + } |
| 471 | + |
| 472 | + if (msg.hasCommitTimestamp()) { |
| 473 | + CommonProtos.VirtualTimestamp vt = msg.getCommitTimestamp(); |
| 474 | + commitVt = new VirtualTimestamp(vt.getPlanStep(), vt.getTxId()); |
| 475 | + } |
| 476 | + |
| 477 | + if (msg.hasSnapshotTimestamp()) { |
| 478 | + CommonProtos.VirtualTimestamp vt = msg.getSnapshotTimestamp(); |
| 479 | + snapshotVt = new VirtualTimestamp(vt.getPlanStep(), vt.getTxId()); |
| 480 | + } |
| 481 | + |
| 482 | + if (msg.hasResultSet()) { |
| 483 | + long index = msg.getResultSetIndex(); |
| 484 | + if (handler != null) { |
| 485 | + handler.onNextRawPart(index, msg.getResultSet()); |
| 486 | + } else { |
| 487 | + logger.trace("{} lost result set part with index {}", SessionImpl.this, index); |
| 488 | + } |
| 489 | + } |
| 490 | + } |
| 491 | + } |
458 | 492 | } |
459 | 493 |
|
460 | 494 | class TransactionImpl extends YdbTransactionImpl implements QueryTransaction { |
@@ -544,23 +578,30 @@ public CompletableFuture<Result<QueryInfo>> commit(CommitTransactionSettings set |
544 | 578 | .build(); |
545 | 579 |
|
546 | 580 | try (Scope ignored = span.makeCurrent()) { |
547 | | - return Span.endOnResult(span, rpc.commitTransaction(request, makeOptions(settings, span).build())) |
548 | | - .thenApply(res -> { |
549 | | - Status status = res.getStatus(); |
550 | | - currentStatusFuture.complete(status); |
551 | | - updateSessionState(status); |
552 | | - if (!txId.compareAndSet(transactionId, null)) { |
553 | | - logger.warn("{} lost commit response for transaction {}", SessionImpl.this, |
554 | | - transactionId); |
555 | | - } |
556 | | - // TODO: CommitTransactionResponse must contain exec_stats |
557 | | - return res.map(resp -> new QueryInfo(null)); |
558 | | - }).whenComplete(((status, th) -> { |
559 | | - if (th != null) { |
560 | | - currentStatusFuture.completeExceptionally( |
561 | | - new RuntimeException("Transaction commit failed with exception", th)); |
562 | | - } |
563 | | - })); |
| 581 | + GrpcRequestSettings options = makeOptions(settings, span).build(); |
| 582 | + CompletableFuture<Result<QueryInfo>> result = rpc.commitTransaction(request, options).thenApply(res -> { |
| 583 | + Status status = res.getStatus(); |
| 584 | + currentStatusFuture.complete(status); |
| 585 | + updateSessionState(status); |
| 586 | + if (!txId.compareAndSet(transactionId, null)) { |
| 587 | + logger.warn("{} lost commit response for transaction {}", SessionImpl.this, transactionId); |
| 588 | + } |
| 589 | + |
| 590 | + return res.map(resp -> { |
| 591 | + VirtualTimestamp commit = null; |
| 592 | + if (resp.hasCommitTimestamp()) { |
| 593 | + CommonProtos.VirtualTimestamp vt = resp.getCommitTimestamp(); |
| 594 | + commit = new VirtualTimestamp(vt.getPlanStep(), vt.getTxId()); |
| 595 | + } |
| 596 | + return new QueryInfo(null, commit, null); |
| 597 | + }); |
| 598 | + }).whenComplete(((status, th) -> { |
| 599 | + if (th != null) { |
| 600 | + currentStatusFuture.completeExceptionally( |
| 601 | + new RuntimeException("Transaction commit failed with exception", th)); |
| 602 | + } |
| 603 | + })); |
| 604 | + return Span.endOnResult(span, result); |
564 | 605 | } |
565 | 606 | } |
566 | 607 |
|
|
0 commit comments