Skip to main content

meta_srv/
error.rs

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