-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy patherrors.proto
597 lines (510 loc) · 24.4 KB
/
errors.proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
// Copyright 2014 The Cockroach Authors.
//
// Licensed 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.
syntax = "proto2";
package cockroach.roachpb;
option go_package = "roachpb";
import "roachpb/data.proto";
import "roachpb/metadata.proto";
import "storage/engine/enginepb/mvcc3.proto";
import "util/hlc/timestamp.proto";
import "gogoproto/gogo.proto";
// Issue #1246. Commented out because
// https://github.com/golang/protobuf/commit/d3d78384b82d449651d2435ed3
// requires that all messages implement Message, which includes
// `String() string`.
// option (gogoproto.goproto_stringer_all) = false;
// A NotLeaseHolderError indicates that the current range is not the lease
// holder. If the lease holder is known, its Replica is set in the error.
message NotLeaseHolderError {
option (gogoproto.equal) = true;
// The replica the error originated from. Used in the error's string
// representation.
optional ReplicaDescriptor replica = 1 [(gogoproto.nullable) = false];
// The lease holder, if known.
optional ReplicaDescriptor lease_holder = 2;
// The current lease, if known. This might be nil even when lease_holder is
// set, as sometimes one can create this error without actually knowing the
// current lease, but having a guess about who the leader is.
optional Lease lease = 4;
optional int64 range_id = 3 [(gogoproto.nullable) = false,
(gogoproto.customname) = "RangeID", (gogoproto.casttype) = "RangeID"];
// If set, the Error() method will return this instead of composing its
// regular spiel. Useful because we reuse this error when rejecting a command
// because the lease under which its application was attempted is different
// than the lease under which it had been proposed.
optional string custom_msg = 5 [(gogoproto.nullable) = false];
}
// A NodeUnavailableError indicates that the sending gateway can
// not process requests at the time, and that the client should
// retry the request with another peer.
message NodeUnavailableError {
option (gogoproto.equal) = true;
}
// An UnsupportedRequestError indicates that the recipient node
// does not know how to handle the type of request received.
message UnsupportedRequestError {
option (gogoproto.equal) = true;
}
// A RangeNotFoundError indicates that a command was sent to a range
// which is not hosted on this store.
message RangeNotFoundError {
option (gogoproto.equal) = true;
optional int64 range_id = 1 [(gogoproto.nullable) = false,
(gogoproto.customname) = "RangeID", (gogoproto.casttype) = "RangeID"];
// store_id is nonzero only if the error originated on a Store.
optional int64 store_id = 2 [(gogoproto.nullable) = false,
(gogoproto.customname) = "StoreID", (gogoproto.casttype) = "StoreID"];
}
// A RangeKeyMismatchError indicates that a command was sent to a
// range which did not contain the key(s) specified by the command.
message RangeKeyMismatchError {
option (gogoproto.equal) = true;
optional bytes request_start_key = 1 [(gogoproto.casttype) = "Key"];
optional bytes request_end_key = 2 [(gogoproto.casttype) = "Key"];
// mismatched_range is the range that the command was incorrectly sent to.
// It is used to update the sender's range cache without an additional range
// lookup.
optional RangeDescriptor mismatched_range = 3;
// suggested_range is a hint to the sender of a command about the range
// they may be looking for. It is only populated when the recipient has
// authoritative knowledge of the range requested by the sender.
optional RangeDescriptor suggested_range = 4;
}
// A ReadWithinUncertaintyIntervalError indicates that a read at timestamp
// encountered a write within the uncertainty interval of the reader.
// The read should be retried at a higher timestamp; the timestamps contained
// within are purely informational, though typically existing_timestamp is a
// lower bound for a new timestamp at which at least the read producing
// this error would succeed on retry.
message ReadWithinUncertaintyIntervalError {
option (gogoproto.equal) = true;
// This data below is purely informational and used to tailor the
// error message.
optional util.hlc.Timestamp read_timestamp = 1 [(gogoproto.nullable) = false];
optional util.hlc.Timestamp existing_timestamp = 2 [(gogoproto.nullable) = false];
// The remaining fields may be missing when running in clusters that have
// members at below CockroachDB v2.0.
optional util.hlc.Timestamp max_timestamp = 3;
repeated ObservedTimestamp observed_timestamps = 4 [(gogoproto.nullable) = false];
}
// TransactionAbortedReason specifies what caused a TransactionAbortedError.
// The reasons below are not necessarily disjoint - they describe where the
// error was generated, but generally it's possible that a
// TransactionAbortedError would have been generated somewhere else if the
// client would have performed different operations.
enum TransactionAbortedReason {
option (gogoproto.goproto_enum_prefix) = false;
// For backwards compatibility.
ABORT_REASON_UNKNOWN = 0;
// A BeginTransaction, HeartbeatTxn, or EndTransaction(commit=true) request
// found an aborted transaction record. Another txn must have written this
// record - that other txn probably ran into one of our intents and pushed our
// transaction record successfully. Either a high-priority transaction simply
// pushed us or we failed to heartbeat for a while and another txn (of any
// priority) considered us abandoned and pushed us.
ABORT_REASON_ABORTED_RECORD_FOUND = 1;
// The request attempting to create a transaction record has a timestamp below
// the TxnSpanGCThreshold, so there might have been an ABORTED txn record that
// got GCed (so, we might be in the ABORT_REASON_ABORTED_RECORD_FOUND case and
// not know).
// TODO(nvanbenschoten): Remove this error reason in 2.3, at which point no
// nodes in the cluster will ever return it.
ABORT_REASON_NEW_TXN_RECORD_TOO_OLD = 2;
// The client is trying to use a transaction that's already been aborted. The
// TxnCoordSender detects this. Either the client is misusing a txn, or the
// TxnCoordSender found out about the transaction being aborted async through
// the heartbeat loop.
ABORT_REASON_CLIENT_REJECT = 3;
// The txn was trying to push another and found out that it itself got aborted
// somehow while waiting for the push.
ABORT_REASON_PUSHER_ABORTED = 4;
// The txn ran into the "abort span" - it was trying to read from a range
// where it had previously laid down intents that have been cleaned up in the
// meantime because the transaction was aborted.
ABORT_REASON_ABORT_SPAN = 5;
// A request attempting to create a transaction record encountered a write
// timestamp cache entry for the txn key, and the entry identifies this
// transaction. This means that the transaction definitely committed or rolled
// back before. So, this request is either a delayed replay of some sort, or
// it raced with an async abort and lost. If a client gets this
// TransactionAbortedError (without it being wrapped in an ambiguous error),
// it must be the latter case, and the transaction can be retried.
ABORT_REASON_ALREADY_COMMITTED_OR_ROLLED_BACK_POSSIBLE_REPLAY = 6;
// Like the above, except the timestamp cache doesn't have a txn id in it.
// This means that it no longer has good accuracy; likely as a result of a
// lease transfer. As above, if the error has not been converted by the time
// it reaches a client, then it's not a replay.
ABORT_REASON_TIMESTAMP_CACHE_REJECTED_POSSIBLE_REPLAY= 7;
}
// A TransactionAbortedError indicates that the client should retry the
// transaction (and use a different txn id, as opposed to
// TransactionRetryError). This most often happens when the transaction was
// aborted by another concurrent transaction. Upon seeing this error, the client
// is supposed to reset its Transaction proto and try the transaction again.
//
// In contrast with other errors, the Transaction that the client gets in the
// pErr carrying this ErrorDetail is not supposed to be used as is by the
// client; the ID should be checked and then attributes like the timestamp
// should be used in creating a new txn.
message TransactionAbortedError {
option (gogoproto.equal) = true;
optional TransactionAbortedReason reason = 1 [(gogoproto.nullable) = false];
}
// A TransactionPushError indicates that the transaction could not
// continue because it encountered a write intent from another
// transaction which it was unable to push.
message TransactionPushError {
option (gogoproto.equal) = true;
optional Transaction pushee_txn = 1 [(gogoproto.nullable) = false];
}
// TransactionRetryReason specifies what caused a transaction retry.
enum TransactionRetryReason {
option (gogoproto.goproto_enum_prefix) = false;
// For backwards compatibility.
RETRY_REASON_UNKNOWN = 0;
// A concurrent writer finished first, causing restart.
RETRY_WRITE_TOO_OLD = 1;
reserved 2;
// A SERIALIZABLE transaction had its timestamp moved forward.
RETRY_SERIALIZABLE = 3;
// A possible replay caused by duplicate begin txn or out-of-order
// txn sequence number.
// TODO(nvanbenschoten): This is no longer in use. Remove in 2.4.
RETRY_POSSIBLE_REPLAY = 4;
// An asynchronous write was observed to have failed.
RETRY_ASYNC_WRITE_FAILURE = 5;
// The transaction exceeded its deadline.
RETRY_COMMIT_DEADLINE_EXCEEDED = 6;
}
// A TransactionRetryError indicates that the transaction must be
// retried, usually with an increased transaction timestamp.
message TransactionRetryError {
option (gogoproto.equal) = true;
optional TransactionRetryReason reason = 1 [(gogoproto.nullable) = false];
optional string extra_msg = 2 [(gogoproto.nullable) = false];
}
// A TransactionStatusError indicates that the transaction status is
// incompatible with the requested operation. This might mean the
// transaction has already been committed. It might also be the case
// that the request to modify the transaction failed due to a
// regression in transaction epoch or timestamp, both of which may
// only monotonically increase.
message TransactionStatusError {
option (gogoproto.equal) = true;
optional string msg = 1 [(gogoproto.nullable) = false];
// Reason specifies what caused the error.
enum Reason {
// For backwards compatibility.
REASON_UNKNOWN = 0;
// The request was sent to a transaction record that does not exist.
REASON_TXN_NOT_FOUND = 1;
// A committed transaction record was found.
REASON_TXN_COMMITTED = 2;
}
optional Reason reason = 2 [(gogoproto.nullable) = false];
}
// A WriteIntentError indicates that one or more write intent
// belonging to another transaction were encountered leading to a
// read/write or write/write conflict. The keys at which the intent
// was encountered are set, as are the txn records for the intents'
// transactions.
message WriteIntentError {
option (gogoproto.equal) = true;
repeated Intent intents = 1 [(gogoproto.nullable) = false];
reserved 2;
}
// A WriteTooOldError indicates that a write encountered a versioned
// value newer than its timestamp, making it impossible to rewrite
// history. The write is instead done at actual timestamp, which is
// the timestamp of the existing version+1.
message WriteTooOldError {
option (gogoproto.equal) = true;
optional util.hlc.Timestamp timestamp = 1 [(gogoproto.nullable) = false];
optional util.hlc.Timestamp actual_timestamp = 2 [(gogoproto.nullable) = false];
}
// An OpRequiresTxnError indicates that a command required to be
// carried out in a transactional context but was not.
// For example, a Scan which spans ranges requires a transaction.
// The operation should be retried inside of a transaction.
message OpRequiresTxnError {
option (gogoproto.equal) = true;
}
// A ConditionFailedError indicates that the expected value
// of a ConditionalPutRequest was not found, either
// because it was missing or was not equal. The error will
// contain the actual value found.
message ConditionFailedError {
option (gogoproto.equal) = true;
optional Value actual_value = 1;
}
// A LeaseRejectedError indicates that the requested replica could
// not acquire the desired lease because of an existing range lease.
message LeaseRejectedError {
option (gogoproto.equal) = true;
optional string message = 1 [(gogoproto.nullable) = false];
optional Lease requested = 2 [(gogoproto.nullable) = false];
optional Lease existing = 3 [(gogoproto.nullable) = false];
}
// A SendError indicates that a message could not be delivered to
// the desired recipient(s).
message SendError {
option (gogoproto.equal) = true;
optional string message = 1 [(gogoproto.nullable) = false];
reserved 2;
}
// An AmbiguousResultError indicates that a request may have succeeded or
// failed, but the response was not received and the final result is ambiguous.
message AmbiguousResultError {
option (gogoproto.equal) = true;
optional string message = 1 [(gogoproto.nullable) = false];
// This can be set to give extra information about which error was converted
// into an AmbiguousResultError. Useful for tests.
optional Error wrapped_err = 2;
}
// A RaftGroupDeletedError indicates a raft group has been deleted for
// the replica.
message RaftGroupDeletedError {
option (gogoproto.equal) = true;
}
// A ReplicaCorruptionError indicates that the replica has experienced
// an error which puts its integrity at risk.
message ReplicaCorruptionError {
option (gogoproto.equal) = true;
optional string error_msg = 1 [(gogoproto.nullable) = false];
// processed indicates that the error has been taken into account and
// necessary steps will be taken. For now, required for testing.
optional bool processed = 2 [(gogoproto.nullable) = false];
}
// ReplicaTooOldError is sent in response to a raft message when the
// recipient of the raft message believes the sender of the raft
// message to have been removed from the raft group
message ReplicaTooOldError {
option (gogoproto.equal) = true;
// replica_id is the ID of the replica that is too old.
optional int32 replica_id = 1 [(gogoproto.nullable) = false,
(gogoproto.customname) = "ReplicaID", (gogoproto.casttype) = "ReplicaID"];
}
// A StoreNotFoundError indicates that a command was sent to a store
// which is not hosted on this node.
message StoreNotFoundError {
option (gogoproto.equal) = true;
optional int64 store_id = 1 [(gogoproto.nullable) = false,
(gogoproto.customname) = "StoreID", (gogoproto.casttype) = "StoreID"];
}
// UnhandledRetryableError tells the recipient that a KV request must be
// retried. In case the request was transactional, the whole transaction needs
// to be retried. This is returned generally as a result of a transaction
// conflict.
//
// This error is generated by pErr.GoError() in case of a retryable
// error (other than TransactionRetryWithProtoRefreshError). For transactional
// requests, the TxnCoordSender handles retryable pErrs and transforms
// them into TransactionRetryWithProtoRefreshError. For non-transactional requests,
// this error will be observed by layers above the TxnCoordSender.
message UnhandledRetryableError {
// The underlying storage error that is being marshaled.
// pErr.TransactionRestart is expected to be set, and the error
// detail is one of the retryable ones.
optional Error pErr = 1 [(gogoproto.nullable) = false];
}
// TransactionRetryWithProtoRefreshError is an error detail representing a retryable error
// that has been "handled" by the TxnCoordSender. This error is produced by the
// TxnCoordSender and is only produced for transactional requests.
//
// It contains the final form of the Transaction proto that should be used on
// next attempts. After being produced by the TxnCoordSender, this error is
// handled first by the client.Txn, which uses the Transaction inside to update
// its state, and then passed along to SQL in a pErr (through the
// client.Sender() interface).
message TransactionRetryWithProtoRefreshError {
option (gogoproto.equal) = true;
// A user-readable message.
optional string msg = 1 [(gogoproto.nullable) = false];
// The ID of the transaction being restarted. The client is supposed to check
// this against the ID of its transaction and make sure the retryable error
// is meant for its level and didn't escape from some inner transaction.
optional bytes txn_id = 2 [
(gogoproto.customtype) = "github.com/cockroachdb/cockroach/pkg/util/uuid.UUID",
(gogoproto.customname) = "TxnID",
(gogoproto.nullable) = false];
// The Transaction that should be used by next attempts. Depending on the
// original cause of this method, this can either be the same Transaction as
// before, but with an incremented epoch and timestamp, or a completely new
// Transaction.
optional Transaction transaction = 3 [(gogoproto.nullable) = false];
}
// TxnAlreadyEncounteredErrorError indicates that an operation tried to use a
// transaction that already received an error from a previous request. Once that
// happens, client.Txn rejects future requests.
message TxnAlreadyEncounteredErrorError{
option (gogoproto.equal) = true;
// prev_error is the message from the error that the txn encountered
// previously.
optional string prev_error = 1 [(gogoproto.nullable) = false];
}
// An IntegerOverflowError indicates that an operation was aborted because
// it would have caused an integeter overflow.
message IntegerOverflowError {
option (gogoproto.equal) = true;
optional bytes key = 1 [(gogoproto.casttype) = "Key"];
optional int64 current_value = 2 [(gogoproto.nullable) = false];
optional int64 increment_value = 3 [(gogoproto.nullable) = false];
}
// A MixedSuccessError indicates that some portion of the batch
// request may have succeeded, but the batch as a whole failed with
// the wrapped error.
message MixedSuccessError {
option (gogoproto.equal) = true;
optional Error wrapped = 1;
}
// A BatchTimestampBeforeGCError indicates that a request's timestamp was
// before the GC threshold.
message BatchTimestampBeforeGCError {
option (gogoproto.equal) = true;
optional util.hlc.Timestamp Timestamp = 1 [(gogoproto.nullable) = false];
optional util.hlc.Timestamp Threshold = 2 [(gogoproto.nullable) = false];
}
// An IntentMissingError indicates that a QueryIntent request expected
// an intent to be present at its specified key but the intent was
// not there.
message IntentMissingError {
option (gogoproto.equal) = true;
// The non-matching intent that was found at that key, if any.
optional Intent wrong_intent = 1;
// The key where the intent was expected.
optional bytes key = 2 [(gogoproto.casttype) = "Key"];
}
// A MergeInProgressError indicates that the request could not be completed
// because the replica is being merged into its left-hand neighbor. The request
// should be resubmitted after the merge completes.
//
// This error is handled by the Store and should not escape to higher levels.
message MergeInProgressError {
option (gogoproto.equal) = true;
}
// A RangeFeedRetryError indicates that a rangefeed was disconnected, often
// because of a range lifecycle event, and can be retried.
message RangeFeedRetryError {
option (gogoproto.equal) = true;
// Reason specifies what caused the error.
enum Reason {
// The replica was removed from its store.
REASON_REPLICA_REMOVED = 0;
// The range was split in two.
REASON_RANGE_SPLIT = 1;
// The range was merged into another.
REASON_RANGE_MERGED = 2;
// A Raft snapshot applied on the replica.
REASON_RAFT_SNAPSHOT = 3;
// A Raft command was missing a logical operation log.
REASON_LOGICAL_OPS_MISSING = 4;
// The consumer was processing events too slowly to keep up with live raft
// events.
REASON_SLOW_CONSUMER = 5;
}
optional Reason reason = 1 [(gogoproto.nullable) = false];
}
// ErrorDetail is a union type containing all available errors.
message ErrorDetail {
option (gogoproto.equal) = true;
reserved 22, 25, 29, 30;
oneof value {
NotLeaseHolderError not_lease_holder = 1;
RangeNotFoundError range_not_found = 2;
RangeKeyMismatchError range_key_mismatch = 3;
ReadWithinUncertaintyIntervalError read_within_uncertainty_interval = 4;
TransactionAbortedError transaction_aborted = 5;
TransactionPushError transaction_push = 6;
TransactionRetryError transaction_retry = 7;
TransactionStatusError transaction_status = 8;
WriteIntentError write_intent = 9;
WriteTooOldError write_too_old = 10;
OpRequiresTxnError op_requires_txn = 11;
ConditionFailedError condition_failed = 12;
LeaseRejectedError lease_rejected = 13;
NodeUnavailableError node_unavailable = 14;
SendError send = 15;
// TODO(kaneda): Following three are added to preserve the type when
// converting Go errors from/to proto Errors. Revisit this design.
RaftGroupDeletedError raft_group_deleted = 16;
ReplicaCorruptionError replica_corruption = 17;
ReplicaTooOldError replica_too_old = 18;
AmbiguousResultError ambiguous_result = 26;
StoreNotFoundError store_not_found = 27;
// The following three are ErrorDetails (and proto messages) because they
// needs to be communicated from the TxnCoordSender and Txn to the upper
// layers through the Sender interface.
TransactionRetryWithProtoRefreshError transaction_retry_with_proto_refresh = 28;
IntegerOverflowError integer_overflow = 31;
UnsupportedRequestError unsupported_request = 32;
MixedSuccessError mixed_success = 33;
BatchTimestampBeforeGCError timestamp_before = 34;
TxnAlreadyEncounteredErrorError txn_already_encountered_error = 35;
IntentMissingError intent_missing = 36;
MergeInProgressError merge_in_progress = 37;
RangeFeedRetryError rangefeed_retry = 38;
}
}
// TransactionRestart indicates how an error should be handled in a
// transactional context.
enum TransactionRestart {
// NONE (the default) is used for errors which have no effect on the
// transaction state. That is, a transactional operation which receives such
// an error is still PENDING and does not need to restart (at least not as a
// result of the error). Examples are a CPut whose condition wasn't met, or
// a spurious RPC error.
NONE = 0;
// BACKOFF is for errors that can retried by restarting the transaction
// after an exponential backoff.
// Note: Deprecated.
BACKOFF = 1;
// IMMEDIATE is for errors that can be retried by restarting the
// transaction immediately.
IMMEDIATE = 2;
}
// ErrPosition describes the position of an error in a Batch. A simple nullable
// primitive field would break compatibility with proto3, where primitive fields
// are no longer allowed to be nullable.
message ErrPosition {
option (gogoproto.equal) = true;
optional int32 index = 1 [(gogoproto.nullable) = false];
}
// Error is a generic representation including a string message
// and information about retryability.
message Error {
option (gogoproto.goproto_stringer) = false;
option (gogoproto.equal) = true;
// message is a human-readable error message.
optional string message = 1 [(gogoproto.nullable) = false];
// If transaction_restart is not ABORT, the error condition may be handled by
// restarting the transaction.
optional TransactionRestart transaction_restart = 3 [(gogoproto.nullable) = false];
// An optional updated transaction. This is to be used by the client in case
// of retryable errors.
//
// Not to be accessed directly - use Error.GetTxn().
optional Transaction unexposed_txn = 4;
// Node at which the error was generated (zero if does not apply).
optional int32 origin_node = 5 [(gogoproto.nullable) = false, (gogoproto.casttype) = "NodeID"];
// If an ErrorDetail is present, it may contain additional structured data
// about the error.
optional ErrorDetail detail = 6 [(gogoproto.nullable) = false];
// The index, if given, contains the index of the request (in the batch)
// whose execution caused the error.
optional ErrPosition index = 7;
// now is the current time at the node sending the response,
// which can be used by the receiver to update its local HLC.
optional util.hlc.Timestamp now = 8 [(gogoproto.nullable) = false];
reserved 2;
}