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