1use common_error::define_into_tonic_status;
16use common_error::ext::{BoxedError, ErrorExt, RetryHint};
17use common_error::status_code::StatusCode;
18use common_macro::stack_trace_debug;
19use common_meta::DatanodeId;
20use common_procedure::ProcedureId;
21use common_runtime::JoinError;
22use common_wal::kafka::rskafka_client_error_to_retry_hint;
23use snafu::{Location, Snafu};
24use store_api::storage::RegionId;
25use table::metadata::TableId;
26use tokio::sync::mpsc::error::SendError;
27use tonic::codegen::http;
28use uuid::Uuid;
29
30use crate::metasrv::SelectTarget;
31use crate::pubsub::Message;
32use crate::service::mailbox::Channel;
33
34#[derive(Snafu)]
35#[snafu(visibility(pub))]
36#[stack_trace_debug]
37pub enum Error {
38 #[snafu(display("Failed to choose items"))]
39 ChooseItems {
40 #[snafu(implicit)]
41 location: Location,
42 #[snafu(source)]
43 error: rand::distr::weighted::Error,
44 },
45
46 #[snafu(display("Exceeded deadline, operation: {}", operation))]
47 ExceededDeadline {
48 #[snafu(implicit)]
49 location: Location,
50 operation: String,
51 },
52
53 #[snafu(display("The target peer is unavailable temporally: {}", peer_id))]
54 PeerUnavailable {
55 #[snafu(implicit)]
56 location: Location,
57 peer_id: u64,
58 },
59
60 #[snafu(display("Failed to list active frontends"))]
61 ListActiveFrontends {
62 #[snafu(implicit)]
63 location: Location,
64 source: common_meta::error::Error,
65 },
66
67 #[snafu(display("Failed to list active datanodes"))]
68 ListActiveDatanodes {
69 #[snafu(implicit)]
70 location: Location,
71 source: common_meta::error::Error,
72 },
73
74 #[snafu(display("Failed to list active flownodes"))]
75 ListActiveFlownodes {
76 #[snafu(implicit)]
77 location: Location,
78 source: common_meta::error::Error,
79 },
80
81 #[snafu(display("No available frontend"))]
82 NoAvailableFrontend {
83 #[snafu(implicit)]
84 location: Location,
85 },
86
87 #[snafu(display("Another migration procedure is running for region: {}", region_id))]
88 MigrationRunning {
89 #[snafu(implicit)]
90 location: Location,
91 region_id: RegionId,
92 },
93
94 #[snafu(display("Another drain procedure is running for datanode: {}", node_id))]
95 DrainDatanodeRunning {
96 #[snafu(implicit)]
97 location: Location,
98 node_id: DatanodeId,
99 },
100
101 #[snafu(display(
102 "The region migration procedure is completed for region: {}, target_peer: {}",
103 region_id,
104 target_peer_id
105 ))]
106 RegionMigrated {
107 #[snafu(implicit)]
108 location: Location,
109 region_id: RegionId,
110 target_peer_id: u64,
111 },
112
113 #[snafu(display("The region migration procedure aborted, reason: {}", reason))]
114 MigrationAbort {
115 #[snafu(implicit)]
116 location: Location,
117 reason: String,
118 },
119
120 #[snafu(display(
121 "Another procedure is operating the region: {} on peer: {}",
122 region_id,
123 peer_id
124 ))]
125 RegionOperatingRace {
126 #[snafu(implicit)]
127 location: Location,
128 peer_id: DatanodeId,
129 region_id: RegionId,
130 },
131
132 #[snafu(display("Failed to init ddl manager"))]
133 InitDdlManager {
134 #[snafu(implicit)]
135 location: Location,
136 source: common_meta::error::Error,
137 },
138
139 #[snafu(display("Failed to init reconciliation manager"))]
140 InitReconciliationManager {
141 #[snafu(implicit)]
142 location: Location,
143 source: common_meta::error::Error,
144 },
145
146 #[snafu(display("Failed to create default catalog and schema"))]
147 InitMetadata {
148 #[snafu(implicit)]
149 location: Location,
150 source: common_meta::error::Error,
151 },
152
153 #[snafu(display("Failed to allocate next sequence number"))]
154 NextSequence {
155 #[snafu(implicit)]
156 location: Location,
157 source: common_meta::error::Error,
158 },
159
160 #[snafu(display("Failed to set next sequence number"))]
161 SetNextSequence {
162 #[snafu(implicit)]
163 location: Location,
164 source: common_meta::error::Error,
165 },
166
167 #[snafu(display("Failed to peek sequence number"))]
168 PeekSequence {
169 #[snafu(implicit)]
170 location: Location,
171 source: common_meta::error::Error,
172 },
173
174 #[snafu(display("Failed to start telemetry task"))]
175 StartTelemetryTask {
176 #[snafu(implicit)]
177 location: Location,
178 source: common_runtime::error::Error,
179 },
180
181 #[snafu(display("Failed to submit ddl task"))]
182 SubmitDdlTask {
183 #[snafu(implicit)]
184 location: Location,
185 source: common_meta::error::Error,
186 },
187
188 #[snafu(display("Failed to submit reconcile procedure"))]
189 SubmitReconcileProcedure {
190 #[snafu(implicit)]
191 location: Location,
192 source: common_meta::error::Error,
193 },
194
195 #[snafu(display("Failed to invalidate table cache"))]
196 InvalidateTableCache {
197 #[snafu(implicit)]
198 location: Location,
199 source: common_meta::error::Error,
200 },
201
202 #[snafu(display("Failed to list catalogs"))]
203 ListCatalogs {
204 #[snafu(implicit)]
205 location: Location,
206 source: BoxedError,
207 },
208
209 #[snafu(display("Failed to list {}'s schemas", catalog))]
210 ListSchemas {
211 #[snafu(implicit)]
212 location: Location,
213 catalog: String,
214 source: BoxedError,
215 },
216
217 #[snafu(display("Failed to list {}.{}'s tables", catalog, schema))]
218 ListTables {
219 #[snafu(implicit)]
220 location: Location,
221 catalog: String,
222 schema: String,
223 source: BoxedError,
224 },
225
226 #[snafu(display("Failed to join a future"))]
227 Join {
228 #[snafu(implicit)]
229 location: Location,
230 #[snafu(source)]
231 error: JoinError,
232 },
233
234 #[snafu(display(
235 "Failed to request {}, required: {}, but only {} available",
236 select_target,
237 required,
238 available
239 ))]
240 NoEnoughAvailableNode {
241 #[snafu(implicit)]
242 location: Location,
243 required: usize,
244 available: usize,
245 select_target: SelectTarget,
246 },
247
248 #[snafu(display("Failed to send shutdown signal"))]
249 SendShutdownSignal {
250 #[snafu(source)]
251 error: SendError<()>,
252 },
253
254 #[snafu(display("Failed to shutdown {} server", server))]
255 ShutdownServer {
256 #[snafu(implicit)]
257 location: Location,
258 source: servers::error::Error,
259 server: String,
260 },
261
262 #[snafu(display("Empty key is not allowed"))]
263 EmptyKey {
264 #[snafu(implicit)]
265 location: Location,
266 },
267
268 #[snafu(display("Failed to execute via Etcd"))]
269 EtcdFailed {
270 #[snafu(source)]
271 error: etcd_client::Error,
272 #[snafu(implicit)]
273 location: Location,
274 },
275
276 #[snafu(display("Failed to connect to Etcd"))]
277 ConnectEtcd {
278 #[snafu(source)]
279 error: etcd_client::Error,
280 #[snafu(implicit)]
281 location: Location,
282 },
283
284 #[snafu(display("Failed to read file: {}", path))]
285 FileIo {
286 #[snafu(source)]
287 error: std::io::Error,
288 #[snafu(implicit)]
289 location: Location,
290 path: String,
291 },
292
293 #[snafu(display("Failed to bind address {}", addr))]
294 TcpBind {
295 addr: String,
296 #[snafu(source)]
297 error: std::io::Error,
298 #[snafu(implicit)]
299 location: Location,
300 },
301
302 #[snafu(display("Failed to start gRPC server"))]
303 StartGrpc {
304 #[snafu(source)]
305 error: tonic::transport::Error,
306 #[snafu(implicit)]
307 location: Location,
308 },
309
310 #[snafu(display("Failed to start http server"))]
311 StartHttp {
312 #[snafu(implicit)]
313 location: Location,
314 source: servers::error::Error,
315 },
316
317 #[snafu(display("Failed to parse address {}", addr))]
318 ParseAddr {
319 addr: String,
320 #[snafu(source)]
321 error: std::net::AddrParseError,
322 },
323
324 #[snafu(display("Invalid lease key: {}", key))]
325 InvalidLeaseKey {
326 key: String,
327 #[snafu(implicit)]
328 location: Location,
329 },
330
331 #[snafu(display("Invalid datanode stat key: {}", key))]
332 InvalidStatKey {
333 key: String,
334 #[snafu(implicit)]
335 location: Location,
336 },
337
338 #[snafu(display("Invalid inactive region key: {}", key))]
339 InvalidInactiveRegionKey {
340 key: String,
341 #[snafu(implicit)]
342 location: Location,
343 },
344
345 #[snafu(display("Invalid node scheduling key: {}", key))]
346 InvalidNodeSchedulingKey {
347 #[snafu(implicit)]
348 location: Location,
349 key: String,
350 },
351
352 #[snafu(display("Failed to parse lease key from utf8"))]
353 LeaseKeyFromUtf8 {
354 #[snafu(source)]
355 error: std::string::FromUtf8Error,
356 #[snafu(implicit)]
357 location: Location,
358 },
359
360 #[snafu(display("Failed to parse lease value from utf8"))]
361 LeaseValueFromUtf8 {
362 #[snafu(source)]
363 error: std::string::FromUtf8Error,
364 #[snafu(implicit)]
365 location: Location,
366 },
367
368 #[snafu(display("Failed to parse invalid region key from utf8"))]
369 InvalidRegionKeyFromUtf8 {
370 #[snafu(source)]
371 error: std::string::FromUtf8Error,
372 #[snafu(implicit)]
373 location: Location,
374 },
375
376 #[snafu(display("Failed to serialize to json: {}", input))]
377 SerializeToJson {
378 input: String,
379 #[snafu(source)]
380 error: serde_json::error::Error,
381 #[snafu(implicit)]
382 location: Location,
383 },
384
385 #[snafu(display("Failed to deserialize from json payload of length {}", input_len))]
386 DeserializeFromJson {
387 input_len: usize,
388 #[snafu(source)]
389 error: serde_json::error::Error,
390 #[snafu(implicit)]
391 location: Location,
392 },
393
394 #[snafu(display("Failed to serialize config"))]
395 SerializeConfig {
396 #[snafu(source)]
397 error: serde_json::error::Error,
398 #[snafu(implicit)]
399 location: Location,
400 },
401
402 #[snafu(display("Failed to parse number: {}", err_msg))]
403 ParseNum {
404 err_msg: String,
405 #[snafu(source)]
406 error: std::num::ParseIntError,
407 #[snafu(implicit)]
408 location: Location,
409 },
410
411 #[snafu(display("Failed to parse bool: {}", err_msg))]
412 ParseBool {
413 err_msg: String,
414 #[snafu(source)]
415 error: std::str::ParseBoolError,
416 #[snafu(implicit)]
417 location: Location,
418 },
419
420 #[snafu(display("Failed to downgrade region leader, region: {}", region_id))]
421 DowngradeLeader {
422 region_id: RegionId,
423 #[snafu(implicit)]
424 location: Location,
425 #[snafu(source)]
426 source: BoxedError,
427 },
428
429 #[snafu(display("Region's leader peer changed: {}", msg))]
430 LeaderPeerChanged {
431 msg: String,
432 #[snafu(implicit)]
433 location: Location,
434 },
435
436 #[snafu(display("Invalid arguments: {}", err_msg))]
437 InvalidArguments {
438 err_msg: String,
439 #[snafu(implicit)]
440 location: Location,
441 },
442
443 #[snafu(display("Manual GC is rejected because maintenance mode is enabled"))]
444 ManualGcRejectedByMaintenanceMode {
445 #[snafu(implicit)]
446 location: Location,
447 },
448
449 #[cfg(feature = "mysql_kvbackend")]
450 #[snafu(display("Failed to parse mysql url: {}", mysql_url))]
451 ParseMySqlUrl {
452 #[snafu(source)]
453 error: sqlx::error::Error,
454 mysql_url: String,
455 #[snafu(implicit)]
456 location: Location,
457 },
458
459 #[cfg(feature = "mysql_kvbackend")]
460 #[snafu(display("Failed to decode sql value"))]
461 DecodeSqlValue {
462 #[snafu(source)]
463 error: sqlx::error::Error,
464 #[snafu(implicit)]
465 location: Location,
466 },
467
468 #[snafu(display("Failed to find table route for {table_id}"))]
469 TableRouteNotFound {
470 table_id: TableId,
471 #[snafu(implicit)]
472 location: Location,
473 },
474
475 #[snafu(display("Failed to find table route for {region_id}"))]
476 RegionRouteNotFound {
477 region_id: RegionId,
478 #[snafu(implicit)]
479 location: Location,
480 },
481
482 #[snafu(display("Table info not found: {}", table_id))]
483 TableInfoNotFound {
484 table_id: TableId,
485 #[snafu(implicit)]
486 location: Location,
487 },
488
489 #[snafu(display("Datanode table not found: {}, datanode: {}", table_id, datanode_id))]
490 DatanodeTableNotFound {
491 table_id: TableId,
492 datanode_id: DatanodeId,
493 #[snafu(implicit)]
494 location: Location,
495 },
496
497 #[snafu(display("Metasrv has no leader at this moment"))]
498 NoLeader {
499 #[snafu(implicit)]
500 location: Location,
501 },
502
503 #[snafu(display("Leader lease expired"))]
504 LeaderLeaseExpired {
505 #[snafu(implicit)]
506 location: Location,
507 },
508
509 #[snafu(display("Leader lease changed during election"))]
510 LeaderLeaseChanged {
511 #[snafu(implicit)]
512 location: Location,
513 },
514
515 #[snafu(display("Table {} not found", name))]
516 TableNotFound {
517 name: String,
518 #[snafu(implicit)]
519 location: Location,
520 },
521
522 #[snafu(display("Unsupported selector type, {}", selector_type))]
523 UnsupportedSelectorType {
524 selector_type: String,
525 #[snafu(implicit)]
526 location: Location,
527 },
528
529 #[snafu(display("Unexpected, violated: {violated}"))]
530 Unexpected {
531 violated: String,
532 #[snafu(implicit)]
533 location: Location,
534 },
535
536 #[snafu(display("Failed to create gRPC channel"))]
537 CreateChannel {
538 #[snafu(implicit)]
539 location: Location,
540 source: common_grpc::error::Error,
541 },
542
543 #[snafu(display("Failed to batch get KVs from leader's in_memory kv store"))]
544 BatchGet {
545 #[snafu(source)]
546 error: tonic::Status,
547 #[snafu(implicit)]
548 location: Location,
549 },
550
551 #[snafu(display("Failed to batch range KVs from leader's in_memory kv store"))]
552 Range {
553 #[snafu(source)]
554 error: tonic::Status,
555 #[snafu(implicit)]
556 location: Location,
557 },
558
559 #[snafu(display("Response header not found"))]
560 ResponseHeaderNotFound {
561 #[snafu(implicit)]
562 location: Location,
563 },
564
565 #[snafu(display("The requested meta node is not leader, node addr: {}", node_addr))]
566 IsNotLeader {
567 node_addr: String,
568 #[snafu(implicit)]
569 location: Location,
570 },
571
572 #[snafu(display("Invalid http body"))]
573 InvalidHttpBody {
574 #[snafu(source)]
575 error: http::Error,
576 #[snafu(implicit)]
577 location: Location,
578 },
579
580 #[snafu(display(
581 "The number of retries for the grpc call {} exceeded the limit, {}",
582 func_name,
583 retry_num
584 ))]
585 ExceededRetryLimit {
586 func_name: String,
587 retry_num: usize,
588 #[snafu(implicit)]
589 location: Location,
590 },
591
592 #[snafu(display("Invalid utf-8 value"))]
593 InvalidUtf8Value {
594 #[snafu(source)]
595 error: std::string::FromUtf8Error,
596 #[snafu(implicit)]
597 location: Location,
598 },
599
600 #[snafu(display("Missing required parameter, param: {:?}", param))]
601 MissingRequiredParameter { param: String },
602
603 #[snafu(display("Failed to start procedure manager"))]
604 StartProcedureManager {
605 #[snafu(implicit)]
606 location: Location,
607 source: common_procedure::Error,
608 },
609
610 #[snafu(display("Failed to stop procedure manager"))]
611 StopProcedureManager {
612 #[snafu(implicit)]
613 location: Location,
614 source: common_procedure::Error,
615 },
616
617 #[snafu(display("Failed to wait procedure done"))]
618 WaitProcedure {
619 #[snafu(implicit)]
620 location: Location,
621 source: common_procedure::Error,
622 },
623
624 #[snafu(display("Failed to query procedure state"))]
625 QueryProcedure {
626 #[snafu(implicit)]
627 location: Location,
628 source: common_procedure::Error,
629 },
630
631 #[snafu(display("Procedure not found: {pid}"))]
632 ProcedureNotFound {
633 #[snafu(implicit)]
634 location: Location,
635 pid: String,
636 },
637
638 #[snafu(display("Failed to submit procedure"))]
639 SubmitProcedure {
640 #[snafu(implicit)]
641 location: Location,
642 source: common_procedure::Error,
643 },
644
645 #[snafu(display("A prune task for topic {} is already running", topic))]
646 PruneTaskAlreadyRunning {
647 topic: String,
648 #[snafu(implicit)]
649 location: Location,
650 },
651
652 #[snafu(display("Schema already exists, name: {schema_name}"))]
653 SchemaAlreadyExists {
654 schema_name: String,
655 #[snafu(implicit)]
656 location: Location,
657 },
658
659 #[snafu(display("Table already exists: {table_name}"))]
660 TableAlreadyExists {
661 table_name: String,
662 #[snafu(implicit)]
663 location: Location,
664 },
665
666 #[snafu(display("Pusher not found: {pusher_id}"))]
667 PusherNotFound {
668 pusher_id: String,
669 #[snafu(implicit)]
670 location: Location,
671 },
672
673 #[snafu(display("Failed to push message: {err_msg}"))]
674 PushMessage {
675 err_msg: String,
676 #[snafu(implicit)]
677 location: Location,
678 },
679
680 #[snafu(display("Mailbox already closed: {id}"))]
681 MailboxClosed {
682 id: u64,
683 #[snafu(implicit)]
684 location: Location,
685 },
686
687 #[snafu(display("Mailbox timeout: {id}"))]
688 MailboxTimeout {
689 id: u64,
690 #[snafu(implicit)]
691 location: Location,
692 },
693
694 #[snafu(display("Mailbox receiver got an error: {id}, {err_msg}"))]
695 MailboxReceiver {
696 id: u64,
697 err_msg: String,
698 #[snafu(implicit)]
699 location: Location,
700 },
701
702 #[snafu(display("Mailbox channel closed: {channel}"))]
703 MailboxChannelClosed {
704 channel: Channel,
705 #[snafu(implicit)]
706 location: Location,
707 },
708
709 #[snafu(display("Missing request header"))]
710 MissingRequestHeader {
711 #[snafu(implicit)]
712 location: Location,
713 },
714
715 #[snafu(display("Failed to register procedure loader, type name: {}", type_name))]
716 RegisterProcedureLoader {
717 type_name: String,
718 #[snafu(implicit)]
719 location: Location,
720 source: common_procedure::error::Error,
721 },
722
723 #[snafu(display(
724 "Received unexpected instruction reply, mailbox message: {}, reason: {}",
725 mailbox_message,
726 reason
727 ))]
728 UnexpectedInstructionReply {
729 mailbox_message: String,
730 reason: String,
731 #[snafu(implicit)]
732 location: Location,
733 },
734
735 #[snafu(display("Expected to retry later, reason: {}", reason))]
736 RetryLater {
737 reason: String,
738 #[snafu(implicit)]
739 location: Location,
740 },
741
742 #[snafu(display("Expected to retry later, reason: {}", reason))]
743 RetryLaterWithSource {
744 reason: String,
745 #[snafu(implicit)]
746 location: Location,
747 source: BoxedError,
748 },
749
750 #[snafu(display("Failed to convert proto data"))]
751 ConvertProtoData {
752 #[snafu(implicit)]
753 location: Location,
754 source: common_meta::error::Error,
755 },
756
757 #[snafu(display("Other error"))]
760 Other {
761 source: BoxedError,
762 #[snafu(implicit)]
763 location: Location,
764 },
765
766 #[snafu(display("Table metadata manager error"))]
767 TableMetadataManager {
768 source: common_meta::error::Error,
769 #[snafu(implicit)]
770 location: Location,
771 },
772
773 #[snafu(display("Runtime switch manager error"))]
774 RuntimeSwitchManager {
775 source: common_meta::error::Error,
776 #[snafu(implicit)]
777 location: Location,
778 },
779
780 #[snafu(display("Keyvalue backend error"))]
781 KvBackend {
782 source: common_meta::error::Error,
783 #[snafu(implicit)]
784 location: Location,
785 },
786
787 #[snafu(display("Failed to publish message"))]
788 PublishMessage {
789 #[snafu(source)]
790 error: SendError<Message>,
791 #[snafu(implicit)]
792 location: Location,
793 },
794
795 #[snafu(display("Too many partitions"))]
796 TooManyPartitions {
797 #[snafu(implicit)]
798 location: Location,
799 },
800
801 #[snafu(display("Failed to create repartition subtasks"))]
802 RepartitionCreateSubtasks {
803 source: partition::error::Error,
804 #[snafu(implicit)]
805 location: Location,
806 },
807
808 #[snafu(display("Failed to manage the repartition GC requirement"))]
809 RepartitionGcRequirement {
810 source: common_meta::error::Error,
811 #[snafu(implicit)]
812 location: Location,
813 },
814
815 #[snafu(display("Failed to inspect persisted repartition procedures"))]
816 InspectRepartitionProcedures {
817 source: common_procedure::Error,
818 #[snafu(implicit)]
819 location: Location,
820 },
821
822 #[snafu(display(
823 "Metasrv GC must be enabled because the cluster has durable repartition state"
824 ))]
825 RepartitionGcRequired {
826 #[snafu(implicit)]
827 location: Location,
828 },
829
830 #[snafu(display(
831 "Source partition expression '{}' does not match any existing region",
832 expr
833 ))]
834 RepartitionSourceExprMismatch {
835 expr: String,
836 #[snafu(implicit)]
837 location: Location,
838 },
839
840 #[snafu(display(
841 "Failed to get the state receiver for repartition subprocedure {}",
842 procedure_id
843 ))]
844 RepartitionSubprocedureStateReceiver {
845 procedure_id: ProcedureId,
846 #[snafu(source)]
847 source: common_procedure::Error,
848 #[snafu(implicit)]
849 location: Location,
850 },
851
852 #[snafu(display("Unsupported operation {}", operation))]
853 Unsupported {
854 operation: String,
855 #[snafu(implicit)]
856 location: Location,
857 },
858
859 #[snafu(display("Unexpected table route type: {}", err_msg))]
860 UnexpectedLogicalRouteTable {
861 #[snafu(implicit)]
862 location: Location,
863 err_msg: String,
864 source: common_meta::error::Error,
865 },
866
867 #[snafu(display("Failed to save cluster info"))]
868 SaveClusterInfo {
869 #[snafu(implicit)]
870 location: Location,
871 source: common_meta::error::Error,
872 },
873
874 #[snafu(display("Invalid cluster info format"))]
875 InvalidClusterInfoFormat {
876 #[snafu(implicit)]
877 location: Location,
878 source: common_meta::error::Error,
879 },
880
881 #[snafu(display("Invalid datanode stat format"))]
882 InvalidDatanodeStatFormat {
883 #[snafu(implicit)]
884 location: Location,
885 source: common_meta::error::Error,
886 },
887
888 #[snafu(display("Invalid node info format"))]
889 InvalidNodeInfoFormat {
890 #[snafu(implicit)]
891 location: Location,
892 source: common_meta::error::Error,
893 },
894
895 #[snafu(display("Failed to serialize options to TOML"))]
896 TomlFormat {
897 #[snafu(implicit)]
898 location: Location,
899 #[snafu(source(from(common_config::error::Error, Box::new)))]
900 source: Box<common_config::error::Error>,
901 },
902
903 #[cfg(feature = "pg_kvbackend")]
904 #[snafu(display("Failed to execute via postgres, sql: {}", sql))]
905 PostgresExecution {
906 #[snafu(source)]
907 error: tokio_postgres::Error,
908 sql: String,
909 #[snafu(implicit)]
910 location: Location,
911 },
912
913 #[cfg(feature = "pg_kvbackend")]
914 #[snafu(display("Failed to get Postgres client"))]
915 GetPostgresClient {
916 #[snafu(implicit)]
917 location: Location,
918 #[snafu(source)]
919 error: deadpool::managed::PoolError<tokio_postgres::Error>,
920 },
921
922 #[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
923 #[snafu(display("Sql execution timeout, sql: {}, duration: {:?}", sql, duration))]
924 SqlExecutionTimeout {
925 #[snafu(implicit)]
926 location: Location,
927 sql: String,
928 duration: std::time::Duration,
929 },
930
931 #[cfg(feature = "pg_kvbackend")]
932 #[snafu(display("Failed to create connection pool for Postgres"))]
933 CreatePostgresPool {
934 #[snafu(source)]
935 error: deadpool_postgres::CreatePoolError,
936 #[snafu(implicit)]
937 location: Location,
938 },
939
940 #[cfg(feature = "pg_kvbackend")]
941 #[snafu(display("Failed to get connection from Postgres pool: {}", reason))]
942 GetPostgresConnection {
943 reason: String,
944 #[snafu(implicit)]
945 location: Location,
946 },
947
948 #[cfg(feature = "mysql_kvbackend")]
949 #[snafu(display("Failed to execute via mysql, sql: {}", sql))]
950 MySqlExecution {
951 #[snafu(source)]
952 error: sqlx::Error,
953 #[snafu(implicit)]
954 location: Location,
955 sql: String,
956 },
957
958 #[cfg(feature = "mysql_kvbackend")]
959 #[snafu(display("Failed to create mysql pool"))]
960 CreateMySqlPool {
961 #[snafu(source)]
962 error: sqlx::Error,
963 #[snafu(implicit)]
964 location: Location,
965 },
966
967 #[cfg(feature = "mysql_kvbackend")]
968 #[snafu(display("Failed to acquire mysql client from pool"))]
969 AcquireMySqlClient {
970 #[snafu(source)]
971 error: sqlx::Error,
972 #[snafu(implicit)]
973 location: Location,
974 },
975
976 #[snafu(display("Handler not found: {}", name))]
977 HandlerNotFound {
978 name: String,
979 #[snafu(implicit)]
980 location: Location,
981 },
982
983 #[snafu(display("Flow state handler error"))]
984 FlowStateHandler {
985 #[snafu(implicit)]
986 location: Location,
987 source: common_meta::error::Error,
988 },
989
990 #[snafu(display("Failed to build wal provider"))]
991 BuildWalProvider {
992 #[snafu(implicit)]
993 location: Location,
994 source: common_meta::error::Error,
995 },
996
997 #[snafu(display("Failed to build kafka client."))]
998 BuildKafkaClient {
999 #[snafu(implicit)]
1000 location: Location,
1001 #[snafu(source)]
1002 error: common_meta::error::Error,
1003 },
1004
1005 #[snafu(display(
1006 "Failed to build a Kafka partition client, topic: {}, partition: {}",
1007 topic,
1008 partition
1009 ))]
1010 BuildPartitionClient {
1011 topic: String,
1012 partition: i32,
1013 #[snafu(implicit)]
1014 location: Location,
1015 #[snafu(source)]
1016 error: rskafka::client::error::Error,
1017 },
1018
1019 #[snafu(display(
1020 "Failed to delete records from Kafka, topic: {}, partition: {}, offset: {}",
1021 topic,
1022 partition,
1023 offset
1024 ))]
1025 DeleteRecords {
1026 #[snafu(implicit)]
1027 location: Location,
1028 #[snafu(source)]
1029 error: rskafka::client::error::Error,
1030 topic: String,
1031 partition: i32,
1032 offset: u64,
1033 },
1034
1035 #[snafu(display("Failed to get offset from Kafka, topic: {}", topic))]
1036 GetOffset {
1037 topic: String,
1038 #[snafu(implicit)]
1039 location: Location,
1040 #[snafu(source)]
1041 error: rskafka::client::error::Error,
1042 },
1043
1044 #[snafu(display("Failed to update the TopicNameValue in kvbackend, topic: {}", topic))]
1045 UpdateTopicNameValue {
1046 topic: String,
1047 #[snafu(implicit)]
1048 location: Location,
1049 #[snafu(source)]
1050 source: common_meta::error::Error,
1051 },
1052
1053 #[snafu(display("Failed to build tls options"))]
1054 BuildTlsOptions {
1055 #[snafu(implicit)]
1056 location: Location,
1057 #[snafu(source)]
1058 source: common_meta::error::Error,
1059 },
1060
1061 #[snafu(display(
1062 "Repartition group {} source region missing, region id: {}",
1063 group_id,
1064 region_id
1065 ))]
1066 RepartitionSourceRegionMissing {
1067 group_id: Uuid,
1068 region_id: RegionId,
1069 #[snafu(implicit)]
1070 location: Location,
1071 },
1072
1073 #[snafu(display(
1074 "Repartition group {} target region missing, region id: {}",
1075 group_id,
1076 region_id
1077 ))]
1078 RepartitionTargetRegionMissing {
1079 group_id: Uuid,
1080 region_id: RegionId,
1081 #[snafu(implicit)]
1082 location: Location,
1083 },
1084
1085 #[snafu(display("Failed to serialize partition expression"))]
1086 SerializePartitionExpr {
1087 #[snafu(source)]
1088 source: partition::error::Error,
1089 #[snafu(implicit)]
1090 location: Location,
1091 },
1092
1093 #[snafu(display("Failed to deserialize partition expression"))]
1094 DeserializePartitionExpr {
1095 #[snafu(source)]
1096 source: partition::error::Error,
1097 #[snafu(implicit)]
1098 location: Location,
1099 },
1100
1101 #[snafu(display("Empty partition expression"))]
1102 EmptyPartitionExpr {
1103 #[snafu(implicit)]
1104 location: Location,
1105 },
1106
1107 #[snafu(display(
1108 "Partition expression mismatch, region id: {}, expected: {}, actual: {}",
1109 region_id,
1110 expected,
1111 actual
1112 ))]
1113 PartitionExprMismatch {
1114 region_id: RegionId,
1115 expected: String,
1116 actual: String,
1117 #[snafu(implicit)]
1118 location: Location,
1119 },
1120
1121 #[snafu(display("Failed to allocate regions for table: {}", table_id))]
1122 AllocateRegions {
1123 #[snafu(implicit)]
1124 location: Location,
1125 table_id: TableId,
1126 #[snafu(source)]
1127 source: common_meta::error::Error,
1128 },
1129
1130 #[snafu(display("Failed to deallocate regions for table: {}", table_id))]
1131 DeallocateRegions {
1132 #[snafu(implicit)]
1133 location: Location,
1134 table_id: TableId,
1135 #[snafu(source)]
1136 source: common_meta::error::Error,
1137 },
1138
1139 #[snafu(display("Failed to build create request for table: {}", table_id))]
1140 BuildCreateRequest {
1141 #[snafu(implicit)]
1142 location: Location,
1143 table_id: TableId,
1144 #[snafu(source)]
1145 source: common_meta::error::Error,
1146 },
1147
1148 #[snafu(display("Failed to allocate region routes for table: {}", table_id))]
1149 AllocateRegionRoutes {
1150 #[snafu(implicit)]
1151 location: Location,
1152 table_id: TableId,
1153 #[snafu(source)]
1154 source: common_meta::error::Error,
1155 },
1156
1157 #[snafu(display("Failed to allocate wal options for table: {}", table_id))]
1158 AllocateWalOptions {
1159 #[snafu(implicit)]
1160 location: Location,
1161 table_id: TableId,
1162 #[snafu(source)]
1163 source: common_meta::error::Error,
1164 },
1165}
1166
1167impl Error {
1168 pub fn is_retryable(&self) -> bool {
1170 self.retry_hint().is_retryable()
1171 }
1172}
1173
1174pub type Result<T> = std::result::Result<T, Error>;
1175
1176define_into_tonic_status!(Error);
1177
1178impl ErrorExt for Error {
1179 fn status_code(&self) -> StatusCode {
1180 match self {
1181 Error::EtcdFailed { .. }
1182 | Error::ConnectEtcd { .. }
1183 | Error::FileIo { .. }
1184 | Error::TcpBind { .. }
1185 | Error::SerializeConfig { .. }
1186 | Error::SerializeToJson { .. }
1187 | Error::DeserializeFromJson { .. }
1188 | Error::NoLeader { .. }
1189 | Error::LeaderLeaseExpired { .. }
1190 | Error::LeaderLeaseChanged { .. }
1191 | Error::CreateChannel { .. }
1192 | Error::BatchGet { .. }
1193 | Error::Range { .. }
1194 | Error::ResponseHeaderNotFound { .. }
1195 | Error::InvalidHttpBody { .. }
1196 | Error::ExceededRetryLimit { .. }
1197 | Error::SendShutdownSignal { .. }
1198 | Error::PushMessage { .. }
1199 | Error::MailboxClosed { .. }
1200 | Error::MailboxReceiver { .. }
1201 | Error::StartGrpc { .. }
1202 | Error::PublishMessage { .. }
1203 | Error::Join { .. }
1204 | Error::ChooseItems { .. }
1205 | Error::FlowStateHandler { .. }
1206 | Error::BuildWalProvider { .. }
1207 | Error::BuildPartitionClient { .. }
1208 | Error::BuildKafkaClient { .. } => StatusCode::Internal,
1209
1210 Error::DeleteRecords { .. }
1211 | Error::GetOffset { .. }
1212 | Error::PeerUnavailable { .. }
1213 | Error::PusherNotFound { .. } => StatusCode::Unexpected,
1214 Error::MailboxTimeout { .. } | Error::ExceededDeadline { .. } => StatusCode::Cancelled,
1215 Error::PruneTaskAlreadyRunning { .. }
1216 | Error::RetryLater { .. }
1217 | Error::MailboxChannelClosed { .. }
1218 | Error::IsNotLeader { .. } => StatusCode::IllegalState,
1219 Error::RetryLaterWithSource { source, .. } => source.status_code(),
1220 Error::SerializePartitionExpr { source, .. }
1221 | Error::DeserializePartitionExpr { source, .. } => source.status_code(),
1222
1223 Error::Unsupported { .. } => StatusCode::Unsupported,
1224
1225 Error::SchemaAlreadyExists { .. } => StatusCode::DatabaseAlreadyExists,
1226
1227 Error::TableAlreadyExists { .. } => StatusCode::TableAlreadyExists,
1228 Error::EmptyKey { .. }
1229 | Error::MissingRequiredParameter { .. }
1230 | Error::MissingRequestHeader { .. }
1231 | Error::InvalidLeaseKey { .. }
1232 | Error::InvalidStatKey { .. }
1233 | Error::InvalidInactiveRegionKey { .. }
1234 | Error::InvalidNodeSchedulingKey { .. }
1235 | Error::ParseNum { .. }
1236 | Error::ParseBool { .. }
1237 | Error::ParseAddr { .. }
1238 | Error::UnsupportedSelectorType { .. }
1239 | Error::InvalidArguments { .. }
1240 | Error::ManualGcRejectedByMaintenanceMode { .. }
1241 | Error::ProcedureNotFound { .. }
1242 | Error::TooManyPartitions { .. }
1243 | Error::TomlFormat { .. }
1244 | Error::HandlerNotFound { .. }
1245 | Error::LeaderPeerChanged { .. }
1246 | Error::RepartitionSourceRegionMissing { .. }
1247 | Error::RepartitionTargetRegionMissing { .. }
1248 | Error::PartitionExprMismatch { .. }
1249 | Error::RepartitionSourceExprMismatch { .. }
1250 | Error::EmptyPartitionExpr { .. } => StatusCode::InvalidArguments,
1251 Error::LeaseKeyFromUtf8 { .. }
1252 | Error::LeaseValueFromUtf8 { .. }
1253 | Error::InvalidRegionKeyFromUtf8 { .. }
1254 | Error::TableRouteNotFound { .. }
1255 | Error::TableInfoNotFound { .. }
1256 | Error::DatanodeTableNotFound { .. }
1257 | Error::InvalidUtf8Value { .. }
1258 | Error::UnexpectedInstructionReply { .. }
1259 | Error::Unexpected { .. }
1260 | Error::RegionOperatingRace { .. }
1261 | Error::RegionRouteNotFound { .. }
1262 | Error::MigrationAbort { .. }
1263 | Error::MigrationRunning { .. }
1264 | Error::DrainDatanodeRunning { .. }
1265 | Error::RegionMigrated { .. } => StatusCode::Unexpected,
1266 Error::TableNotFound { .. } => StatusCode::TableNotFound,
1267 Error::SaveClusterInfo { source, .. }
1268 | Error::InvalidClusterInfoFormat { source, .. }
1269 | Error::InvalidDatanodeStatFormat { source, .. }
1270 | Error::InvalidNodeInfoFormat { source, .. } => source.status_code(),
1271 Error::InvalidateTableCache { source, .. } => source.status_code(),
1272 Error::SubmitProcedure { source, .. }
1273 | Error::WaitProcedure { source, .. }
1274 | Error::QueryProcedure { source, .. } => source.status_code(),
1275 Error::ShutdownServer { source, .. } | Error::StartHttp { source, .. } => {
1276 source.status_code()
1277 }
1278 Error::StartProcedureManager { source, .. }
1279 | Error::StopProcedureManager { source, .. } => source.status_code(),
1280
1281 Error::ListCatalogs { source, .. }
1282 | Error::ListSchemas { source, .. }
1283 | Error::ListTables { source, .. } => source.status_code(),
1284 Error::StartTelemetryTask { source, .. } => source.status_code(),
1285
1286 Error::NextSequence { source, .. }
1287 | Error::SetNextSequence { source, .. }
1288 | Error::PeekSequence { source, .. } => source.status_code(),
1289 Error::DowngradeLeader { source, .. } => source.status_code(),
1290 Error::RegisterProcedureLoader { source, .. } => source.status_code(),
1291 Error::SubmitDdlTask { source, .. }
1292 | Error::SubmitReconcileProcedure { source, .. } => source.status_code(),
1293 Error::ConvertProtoData { source, .. }
1294 | Error::TableMetadataManager { source, .. }
1295 | Error::RuntimeSwitchManager { source, .. }
1296 | Error::KvBackend { source, .. }
1297 | Error::UnexpectedLogicalRouteTable { source, .. }
1298 | Error::UpdateTopicNameValue { source, .. } => source.status_code(),
1299 Error::ListActiveFrontends { source, .. }
1300 | Error::ListActiveDatanodes { source, .. }
1301 | Error::ListActiveFlownodes { source, .. } => source.status_code(),
1302 Error::NoAvailableFrontend { .. } | Error::RepartitionGcRequired { .. } => {
1303 StatusCode::IllegalState
1304 }
1305
1306 Error::InitMetadata { source, .. }
1307 | Error::InitDdlManager { source, .. }
1308 | Error::InitReconciliationManager { source, .. } => source.status_code(),
1309
1310 Error::BuildTlsOptions { source, .. } => source.status_code(),
1311 Error::Other { source, .. } => source.status_code(),
1312 Error::RepartitionCreateSubtasks { source, .. } => source.status_code(),
1313 Error::RepartitionGcRequirement { source, .. } => source.status_code(),
1314 Error::InspectRepartitionProcedures { source, .. } => source.status_code(),
1315 Error::RepartitionSubprocedureStateReceiver { source, .. } => source.status_code(),
1316 Error::AllocateRegions { source, .. } => source.status_code(),
1317 Error::DeallocateRegions { source, .. } => source.status_code(),
1318 Error::AllocateRegionRoutes { source, .. } => source.status_code(),
1319 Error::AllocateWalOptions { source, .. } => source.status_code(),
1320 Error::BuildCreateRequest { source, .. } => source.status_code(),
1321 Error::NoEnoughAvailableNode { .. } => StatusCode::RuntimeResourcesExhausted,
1322
1323 #[cfg(feature = "pg_kvbackend")]
1324 Error::CreatePostgresPool { .. }
1325 | Error::GetPostgresClient { .. }
1326 | Error::GetPostgresConnection { .. }
1327 | Error::PostgresExecution { .. } => StatusCode::Internal,
1328 #[cfg(feature = "mysql_kvbackend")]
1329 Error::MySqlExecution { .. }
1330 | Error::CreateMySqlPool { .. }
1331 | Error::ParseMySqlUrl { .. }
1332 | Error::DecodeSqlValue { .. }
1333 | Error::AcquireMySqlClient { .. } => StatusCode::Internal,
1334 #[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
1335 Error::SqlExecutionTimeout { .. } => StatusCode::Internal,
1336 }
1337 }
1338
1339 fn as_any(&self) -> &dyn std::any::Any {
1340 self
1341 }
1342
1343 fn retry_hint(&self) -> RetryHint {
1344 match self {
1345 Error::RetryLater { .. }
1346 | Error::RetryLaterWithSource { .. }
1347 | Error::MailboxTimeout { .. }
1348 | Error::NoEnoughAvailableNode { .. }
1349 | Error::NoLeader { .. }
1350 | Error::LeaderLeaseExpired { .. }
1351 | Error::LeaderLeaseChanged { .. }
1352 | Error::PeerUnavailable { .. } => RetryHint::Retryable,
1353
1354 Error::ConnectEtcd { error, .. } | Error::EtcdFailed { error, .. } => {
1355 common_meta::error::retry_hint_from_etcd_error(error)
1356 }
1357
1358 #[cfg(feature = "pg_kvbackend")]
1359 Error::PostgresExecution { error, .. } => {
1360 common_meta::error::retry_hint_from_postgres_error(error)
1361 }
1362 #[cfg(feature = "pg_kvbackend")]
1363 Error::GetPostgresClient { error, .. } => {
1364 common_meta::error::retry_hint_from_postgres_pool_error(error)
1365 }
1366 #[cfg(feature = "mysql_kvbackend")]
1367 Error::MySqlExecution { error, .. }
1368 | Error::CreateMySqlPool { error, .. }
1369 | Error::AcquireMySqlClient { error, .. } => {
1370 common_meta::error::retry_hint_from_sqlx_error(error)
1371 }
1372 #[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
1373 Error::SqlExecutionTimeout { .. } => RetryHint::Retryable,
1374
1375 Error::ListActiveFrontends { source, .. }
1376 | Error::ListActiveDatanodes { source, .. }
1377 | Error::ListActiveFlownodes { source, .. }
1378 | Error::InitDdlManager { source, .. }
1379 | Error::InitReconciliationManager { source, .. }
1380 | Error::InitMetadata { source, .. }
1381 | Error::NextSequence { source, .. }
1382 | Error::SetNextSequence { source, .. }
1383 | Error::PeekSequence { source, .. }
1384 | Error::SubmitDdlTask { source, .. }
1385 | Error::SubmitReconcileProcedure { source, .. }
1386 | Error::InvalidateTableCache { source, .. }
1387 | Error::ConvertProtoData { source, .. }
1388 | Error::TableMetadataManager { source, .. }
1389 | Error::RuntimeSwitchManager { source, .. }
1390 | Error::KvBackend { source, .. }
1391 | Error::UnexpectedLogicalRouteTable { source, .. }
1392 | Error::SaveClusterInfo { source, .. }
1393 | Error::InvalidClusterInfoFormat { source, .. }
1394 | Error::InvalidDatanodeStatFormat { source, .. }
1395 | Error::InvalidNodeInfoFormat { source, .. }
1396 | Error::FlowStateHandler { source, .. }
1397 | Error::BuildWalProvider { source, .. }
1398 | Error::BuildKafkaClient { error: source, .. }
1399 | Error::UpdateTopicNameValue { source, .. }
1400 | Error::BuildTlsOptions { source, .. }
1401 | Error::AllocateRegions { source, .. }
1402 | Error::DeallocateRegions { source, .. }
1403 | Error::BuildCreateRequest { source, .. }
1404 | Error::AllocateRegionRoutes { source, .. }
1405 | Error::AllocateWalOptions { source, .. }
1406 | Error::RepartitionGcRequirement { source, .. } => source.retry_hint(),
1407
1408 Error::Other { source, .. }
1409 | Error::ListCatalogs { source, .. }
1410 | Error::ListSchemas { source, .. }
1411 | Error::ListTables { source, .. }
1412 | Error::DowngradeLeader { source, .. } => source.retry_hint(),
1413
1414 Error::SubmitProcedure { source, .. }
1415 | Error::WaitProcedure { source, .. }
1416 | Error::QueryProcedure { source, .. }
1417 | Error::StartProcedureManager { source, .. }
1418 | Error::StopProcedureManager { source, .. }
1419 | Error::RegisterProcedureLoader { source, .. }
1420 | Error::InspectRepartitionProcedures { source, .. }
1421 | Error::RepartitionSubprocedureStateReceiver { source, .. } => source.retry_hint(),
1422
1423 Error::ShutdownServer { source, .. } | Error::StartHttp { source, .. } => {
1424 source.retry_hint()
1425 }
1426 Error::StartTelemetryTask { source, .. } => source.retry_hint(),
1427 Error::CreateChannel { source, .. } => source.retry_hint(),
1428 Error::RepartitionCreateSubtasks { source, .. } => source.retry_hint(),
1429 Error::SerializePartitionExpr { source, .. }
1430 | Error::DeserializePartitionExpr { source, .. } => source.retry_hint(),
1431
1432 Error::DeleteRecords { error, .. }
1433 | Error::BuildPartitionClient { error, .. }
1434 | Error::GetOffset { error, .. } => rskafka_client_error_to_retry_hint(error),
1435
1436 Error::PusherNotFound { .. }
1437 | Error::PushMessage { .. }
1438 | Error::ExceededDeadline { .. } => RetryHint::NonRetryable,
1439
1440 _ => RetryHint::NonRetryable,
1441 }
1442 }
1443}
1444
1445pub(crate) fn match_for_io_error(err_status: &tonic::Status) -> Option<&std::io::Error> {
1447 let mut err: &(dyn std::error::Error + 'static) = err_status;
1448
1449 loop {
1450 if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
1451 return Some(io_err);
1452 }
1453
1454 if let Some(h2_err) = err.downcast_ref::<h2::Error>()
1457 && let Some(io_err) = h2_err.get_io()
1458 {
1459 return Some(io_err);
1460 }
1461
1462 err = err.source()?;
1463 }
1464}
1465
1466#[cfg(test)]
1467mod tests {
1468 use std::time::Duration;
1469
1470 use common_error::ext::ErrorExt;
1471 use common_error::mock::MockError;
1472 use common_error::status_code::StatusCode;
1473 use rskafka::BackoffError;
1474 use rskafka::client::error::Error as KafkaClientError;
1475 use snafu::ResultExt;
1476
1477 use super::{
1478 BuildPartitionClientSnafu, DeallocateRegionsSnafu, DeleteRecordsSnafu, GetOffsetSnafu,
1479 };
1480
1481 fn retry_failed_kafka_error() -> KafkaClientError {
1482 KafkaClientError::RetryFailed(BackoffError::DeadlineExceded {
1483 deadline: Duration::from_secs(1),
1484 source: Box::new(std::io::Error::other("retry failed")),
1485 })
1486 }
1487
1488 #[test]
1489 fn test_deallocate_regions_is_retryable_when_source_is_retry_later() {
1490 let source = common_meta::error::Error::retry_later(MockError::new(StatusCode::Internal));
1491 let err = Err::<(), _>(source)
1492 .context(DeallocateRegionsSnafu { table_id: 1024_u32 })
1493 .unwrap_err();
1494
1495 assert!(err.is_retryable());
1496 assert!(err.retry_hint().is_retryable());
1497 }
1498
1499 #[test]
1500 fn test_deallocate_regions_is_not_retryable_when_source_is_not_retry_later() {
1501 let source = common_meta::error::UnexpectedSnafu {
1502 err_msg: "mock error",
1503 }
1504 .build();
1505 let err = Err::<(), _>(source)
1506 .context(DeallocateRegionsSnafu { table_id: 1024_u32 })
1507 .unwrap_err();
1508
1509 assert!(!err.is_retryable());
1510 assert!(!err.retry_hint().is_retryable());
1511 }
1512
1513 #[test]
1514 fn test_kafka_retry_failed_errors_are_retryable() {
1515 let delete_records_err = Err::<(), _>(retry_failed_kafka_error())
1516 .context(DeleteRecordsSnafu {
1517 topic: "test_topic",
1518 partition: 0,
1519 offset: 1024u64,
1520 })
1521 .unwrap_err();
1522 let build_partition_client_err = Err::<(), _>(retry_failed_kafka_error())
1523 .context(BuildPartitionClientSnafu {
1524 topic: "test_topic",
1525 partition: 0,
1526 })
1527 .unwrap_err();
1528 let get_offset_err = Err::<(), _>(retry_failed_kafka_error())
1529 .context(GetOffsetSnafu {
1530 topic: "test_topic",
1531 })
1532 .unwrap_err();
1533
1534 assert!(delete_records_err.is_retryable());
1535 assert!(build_partition_client_err.is_retryable());
1536 assert!(get_offset_err.is_retryable());
1537 assert!(delete_records_err.retry_hint().is_retryable());
1538 assert!(build_partition_client_err.retry_hint().is_retryable());
1539 assert!(get_offset_err.retry_hint().is_retryable());
1540 }
1541
1542 #[test]
1543 fn test_kafka_non_retry_failed_errors_are_not_retryable() {
1544 let delete_records_err = Err::<(), _>(KafkaClientError::InvalidResponse("invalid".into()))
1545 .context(DeleteRecordsSnafu {
1546 topic: "test_topic",
1547 partition: 0,
1548 offset: 1024u64,
1549 })
1550 .unwrap_err();
1551 let build_partition_client_err =
1552 Err::<(), _>(KafkaClientError::InvalidResponse("invalid".into()))
1553 .context(BuildPartitionClientSnafu {
1554 topic: "test_topic",
1555 partition: 0,
1556 })
1557 .unwrap_err();
1558 let get_offset_err = Err::<(), _>(KafkaClientError::InvalidResponse("invalid".into()))
1559 .context(GetOffsetSnafu {
1560 topic: "test_topic",
1561 })
1562 .unwrap_err();
1563
1564 assert!(!delete_records_err.is_retryable());
1565 assert!(!build_partition_client_err.is_retryable());
1566 assert!(!get_offset_err.is_retryable());
1567 assert!(!delete_records_err.retry_hint().is_retryable());
1568 assert!(!build_partition_client_err.retry_hint().is_retryable());
1569 assert!(!get_offset_err.retry_hint().is_retryable());
1570 }
1571}