1mod alter_user_scram_credentials;
16mod consumer_group_describe;
17mod create_acls;
18mod create_topics;
19mod delete_groups;
20mod delete_records;
21mod delete_topics;
22mod describe_acls;
23mod describe_cluster;
24mod describe_configs;
25mod describe_groups;
26mod describe_topic_partitions;
27mod describe_user_scram_credentials;
28mod fetch;
29mod find_coordinator;
30mod get_telemetry_subscriptions;
31mod incremental_alter_configs;
32mod init_producer_id;
33mod list_groups;
34mod list_offsets;
35mod list_partition_reassignments;
36mod metadata;
37mod produce;
38mod txn;
39
40use std::{
41 collections::BTreeMap,
42 fmt::{self, Debug, Display, Formatter},
43 sync::LazyLock,
44 time::{Duration, SystemTime},
45};
46
47pub use alter_user_scram_credentials::AlterUserScramCredentialsService;
48use async_trait::async_trait;
49pub use consumer_group_describe::ConsumerGroupDescribeService;
50pub use create_acls::CreateAclsService;
51pub use create_topics::CreateTopicsService;
52pub use delete_groups::DeleteGroupsService;
53pub use delete_records::DeleteRecordsService;
54pub use delete_topics::DeleteTopicsService;
55pub use describe_acls::DescribeAclsService;
56pub use describe_cluster::DescribeClusterService;
57pub use describe_configs::DescribeConfigsService;
58pub use describe_groups::DescribeGroupsService;
59pub use describe_topic_partitions::DescribeTopicPartitionsService;
60pub use describe_user_scram_credentials::DescribeUserScramCredentialsService;
61pub use fetch::FetchService;
62pub use find_coordinator::FindCoordinatorService;
63pub use get_telemetry_subscriptions::GetTelemetrySubscriptionsService;
64pub use incremental_alter_configs::IncrementalAlterConfigsService;
65pub use init_producer_id::InitProducerIdService;
66pub use list_groups::ListGroupsService;
67pub use list_offsets::ListOffsetsService;
68pub use list_partition_reassignments::ListPartitionReassignmentsService;
69pub use metadata::MetadataService;
70use nisshi_sans_io::{
71 ConfigResource, ErrorCode, IsolationLevel, ListOffset, ScramMechanism,
72 create_topics_request::CreatableTopic, delete_groups_response::DeletableGroupResult,
73 delete_records_request::DeleteRecordsTopic, delete_records_response::DeleteRecordsTopicResult,
74 describe_cluster_response::DescribeClusterBroker,
75 describe_configs_response::DescribeConfigsResult,
76 describe_topic_partitions_response::DescribeTopicPartitionsResponseTopic,
77 incremental_alter_configs_request::AlterConfigsResource,
78 incremental_alter_configs_response::AlterConfigsResourceResponse,
79 list_groups_response::ListedGroup, record::deflated,
80 txn_offset_commit_response::TxnOffsetCommitResponseTopic,
81};
82use opentelemetry::{
83 KeyValue,
84 metrics::{Counter, Gauge, Histogram},
85};
86pub use produce::ProduceService;
87use rama::{Context, Layer, Service};
88use tokio::sync::{
89 mpsc::{self, error::SendError},
90 oneshot,
91};
92use tokio_util::sync::CancellationToken;
93use tracing::{debug, error, instrument};
94pub use txn::add_offsets::AddOffsetsService as TxnAddOffsetsService;
95pub use txn::add_partitions::AddPartitionService as TxnAddPartitionService;
96pub use txn::end::EndService as TxnEndService;
97pub use txn::offset_commit::OffsetCommitService as TxnOffsetCommitService;
98use url::Url;
99use uuid::Uuid;
100
101use crate::{
102 BrokerRegistrationRequest, Error, GroupDetail, ListOffsetResponse, METER, MetadataResponse,
103 NamedGroupDetail, OffsetCommitRequest, OffsetStage, ProducerIdResponse, Result,
104 ScramCredential, Storage, TopicId, Topition, TxnAddPartitionsRequest, TxnAddPartitionsResponse,
105 TxnOffsetCommitRequest, UpdateError, Version,
106};
107
108#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
109pub enum Request {
110 RegisterBroker(BrokerRegistrationRequest),
111 IncrementalAlterResource(AlterConfigsResource),
112 CreateTopic {
113 topic: CreatableTopic,
114 validate_only: bool,
115 },
116 DeleteRecords(Vec<DeleteRecordsTopic>),
117 DeleteTopic(TopicId),
118 Brokers,
119 Produce {
120 transaction_id: Option<String>,
121 topition: Topition,
122 batch: deflated::Batch,
123 },
124 Fetch {
125 topition: Topition,
126 offset: i64,
127 min_bytes: u32,
128 max_bytes: u32,
129 isolation: IsolationLevel,
130 max_wait: Duration,
131 },
132 OffsetStage(Topition),
133 ListOffsets {
134 isolation_level: IsolationLevel,
135 offsets: Vec<(Topition, ListOffset)>,
136 },
137 OffsetCommit {
138 group_id: String,
139 retention_time_ms: Option<Duration>,
140 offsets: Vec<(Topition, OffsetCommitRequest)>,
141 },
142 CommittedOffsetTopitions(String),
143 OffsetFetch {
144 group_id: Option<String>,
145 topics: Vec<Topition>,
146 require_stable: Option<bool>,
147 },
148 Metadata(Option<Vec<TopicId>>),
149 DescribeConfig {
150 name: String,
151 resource: ConfigResource,
152 keys: Option<Vec<String>>,
153 },
154 DescribeTopicPartitions {
155 topics: Option<Vec<TopicId>>,
156 partition_limit: i32,
157 cursor: Option<Topition>,
158 },
159 ListGroups(Option<Vec<String>>),
160 DeleteGroups(Option<Vec<String>>),
161 DescribeGroups {
162 group_ids: Option<Vec<String>>,
163 include_authorized_operations: bool,
164 },
165 UpdateGroup {
166 group_id: String,
167 detail: GroupDetail,
168 version: Option<Version>,
169 },
170 InitProducer {
171 transaction_id: Option<String>,
172 transaction_timeout_ms: i32,
173 producer_id: Option<i64>,
174 producer_epoch: Option<i16>,
175 },
176 TxnAddOffsets {
177 transaction_id: String,
178 producer_id: i64,
179 producer_epoch: i16,
180 group_id: String,
181 },
182 TxnAddPartitions(TxnAddPartitionsRequest),
183 TxnOffsetCommit(TxnOffsetCommitRequest),
184 TxnEnd {
185 transaction_id: String,
186 producer_id: i64,
187 producer_epoch: i16,
188 committed: bool,
189 },
190 Maintain(SystemTime),
191 ClusterId,
192 Node,
193 AdvertisedListener,
194 DeleteUserScramCredential {
195 user: String,
196 mechanism: ScramMechanism,
197 },
198 UpsertUserScramCredential {
199 user: String,
200 mechanism: ScramMechanism,
201 credential: ScramCredential,
202 },
203 UserScramCredential {
204 user: String,
205 mechanism: ScramMechanism,
206 },
207 Ping,
208}
209
210impl Display for Request {
211 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
212 match self {
213 Self::AdvertisedListener => f.write_str("AdvertisedListener"),
214 Self::Brokers => f.write_str("Brokers"),
215 Self::ClusterId => f.write_str("ClusterId"),
216 Self::CommittedOffsetTopitions(_) => f.write_str("CommittedOffsetTopitions"),
217 Self::CreateTopic { .. } => f.write_str("CreateTopic"),
218 Self::DeleteGroups(_) => f.write_str("DeleteGroups"),
219 Self::DeleteRecords(_) => f.write_str("DeleteRecords"),
220 Self::DeleteTopic(_) => f.write_str("DeleteTopic"),
221 Self::DescribeConfig { .. } => f.write_str("DescribeConfig"),
222 Self::DescribeGroups { .. } => f.write_str("DescribeGroups"),
223 Self::DescribeTopicPartitions { .. } => f.write_str("DescribeTopicPartitions"),
224 Self::Fetch { .. } => f.write_str("Fetch"),
225 Self::IncrementalAlterResource(_) => f.write_str("IncrementalAlterResource"),
226 Self::InitProducer { .. } => f.write_str("InitProducer"),
227 Self::ListGroups(_) => f.write_str("ListGroups"),
228 Self::ListOffsets { .. } => f.write_str("ListOffsets"),
229 Self::Maintain(_) => f.write_str("Maintain"),
230 Self::Metadata(_) => f.write_str("Metadata"),
231 Self::Node => f.write_str("Node"),
232 Self::OffsetCommit { .. } => f.write_str("OffsetCommit"),
233 Self::OffsetFetch { .. } => f.write_str("OffsetFetch"),
234 Self::OffsetStage(_) => f.write_str("OffsetStage"),
235 Self::Produce { .. } => f.write_str("Produce"),
236 Self::RegisterBroker(_) => f.write_str("RegisterBroker"),
237 Self::TxnAddOffsets { .. } => f.write_str("TxnAddOffsets"),
238 Self::TxnAddPartitions(_) => f.write_str("TxnAddPartitions"),
239 Self::TxnEnd { .. } => f.write_str("TxnEnd"),
240 Self::TxnOffsetCommit(_) => f.write_str("TxnOffsetCommit"),
241 Self::UpdateGroup { .. } => f.write_str("UpdateGroup"),
242 Self::DeleteUserScramCredential { .. } => f.write_str("DeleteUserScramCredential"),
243 Self::UpsertUserScramCredential { .. } => f.write_str("UpsertUserScramCredential"),
244 Self::UserScramCredential { .. } => f.write_str("UserScramCredential"),
245 Self::Ping => f.write_str("Ping"),
246 }
247 }
248}
249
250#[derive(Clone, Debug)]
251pub enum Response {
252 AdvertisedListener(Result<Url>),
253 Brokers(Result<Vec<DescribeClusterBroker>>),
254 ClusterId(Result<String>),
255 CommittedOffsetTopitions(Result<BTreeMap<Topition, i64>>),
256 CreateTopic(Result<Uuid>),
257 DeleteGroups(Result<Vec<DeletableGroupResult>>),
258 DeleteRecords(Result<Vec<DeleteRecordsTopicResult>>),
259 DeleteTopic(Result<ErrorCode>),
260 DeleteUserScramCredential(Result<()>),
261 DescribeConfig(Result<DescribeConfigsResult>),
262 DescribeGroups(Result<Vec<NamedGroupDetail>>),
263 DescribeTopicPartitions(Result<Vec<DescribeTopicPartitionsResponseTopic>>),
264 Fetch(Result<Vec<deflated::Batch>>),
265 IncrementalAlterResponse(Result<AlterConfigsResourceResponse>),
266 InitProducer(Result<ProducerIdResponse>),
267 ListGroups(Result<Vec<ListedGroup>>),
268 ListOffsets(Result<Vec<(Topition, ListOffsetResponse)>>),
269 Maintain(Result<()>),
270 Metadata(Result<MetadataResponse>),
271 Node(Result<i32>),
272 OffsetCommit(Result<Vec<(Topition, ErrorCode)>>),
273 OffsetFetch(Result<BTreeMap<Topition, i64>>),
274 OffsetStage(Result<OffsetStage>),
275 Ping(Result<()>),
276 Produce(Result<i64>),
277 RegisterBroker(Result<()>),
278 TxnAddOffsets(Result<ErrorCode>),
279 TxnAddPartitions(Result<TxnAddPartitionsResponse>),
280 TxnEnd(Result<ErrorCode>),
281 TxnOffsetCommit(Result<Vec<TxnOffsetCommitResponseTopic>>),
282 UpdateGroup(Result<Version, UpdateError<GroupDetail>>),
283 UpsertUserScramCredential(Result<()>),
284 UserScramCredential(Result<Option<ScramCredential>>),
285}
286
287pub type RequestSender = mpsc::Sender<(Request, oneshot::Sender<Response>)>;
288pub type RequestReceiver = mpsc::Receiver<(Request, oneshot::Sender<Response>)>;
289
290pub fn bounded_channel(buffer: usize) -> (RequestSender, RequestReceiver) {
291 mpsc::channel::<(Request, oneshot::Sender<Response>)>(buffer)
292}
293
294#[derive(Clone, Debug, thiserror::Error)]
295pub enum ServiceError {
296 Storage(Error),
297 UpdateGroupDetail(UpdateError<GroupDetail>),
298}
299
300impl Display for ServiceError {
301 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
302 write!(f, "{self:?}")
303 }
304}
305
306impl From<SendError<()>> for ServiceError {
307 fn from(_value: SendError<()>) -> Self {
308 Self::Storage(Error::UnableToSend)
309 }
310}
311
312impl From<Error> for ServiceError {
313 fn from(value: Error) -> Self {
314 Self::Storage(value)
315 }
316}
317
318impl From<UpdateError<GroupDetail>> for ServiceError {
319 fn from(value: UpdateError<GroupDetail>) -> Self {
320 Self::UpdateGroupDetail(value)
321 }
322}
323
324impl From<ServiceError> for Error {
325 fn from(value: ServiceError) -> Self {
326 if let ServiceError::Storage(error) = value {
327 error
328 } else {
329 unreachable!()
330 }
331 }
332}
333
334impl From<ServiceError> for UpdateError<GroupDetail> {
335 fn from(value: ServiceError) -> Self {
336 if let ServiceError::UpdateGroupDetail(error) = value {
337 error
338 } else {
339 unreachable!()
340 }
341 }
342}
343
344#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
345pub struct RequestLayer;
346
347impl<S> Layer<S> for RequestLayer {
348 type Service = RequestService<S>;
349
350 fn layer(&self, inner: S) -> Self::Service {
351 Self::Service { inner }
352 }
353}
354
355#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
356pub struct RequestService<S> {
357 inner: S,
358}
359
360impl<State, S> Service<State, Request> for RequestService<S>
361where
362 S: Service<State, Request>,
363 State: Send + Sync + 'static,
364{
365 type Response = S::Response;
366 type Error = S::Error;
367
368 async fn serve(
369 &self,
370 ctx: Context<State>,
371 req: Request,
372 ) -> Result<Self::Response, Self::Error> {
373 debug!(?req);
374 self.inner.serve(ctx, req).await
375 }
376}
377
378#[derive(Clone, Debug)]
380pub struct RequestChannelService {
381 tx: RequestSender,
382}
383
384impl RequestChannelService {
385 pub fn new(tx: RequestSender) -> Self {
386 Self { tx }
387 }
388
389 fn elapsed_millis(&self, start: SystemTime) -> u64 {
390 start
391 .elapsed()
392 .map_or(0, |duration| duration.as_millis() as u64)
393 }
394}
395
396static STORAGE_CHANNEL_CAPACITY: LazyLock<Gauge<u64>> = LazyLock::new(|| {
397 METER
398 .u64_gauge("nisshi_storage_channel_capacity")
399 .with_description("Storage channel capacity")
400 .build()
401});
402
403impl<State> Service<State, Request> for RequestChannelService
404where
405 State: Send + Sync + 'static,
406{
407 type Response = Response;
408 type Error = ServiceError;
409
410 #[instrument(skip_all)]
411 async fn serve(
412 &self,
413 ctx: Context<State>,
414 req: Request,
415 ) -> Result<Self::Response, Self::Error> {
416 let _ = ctx;
417 let (resp_tx, resp_rx) = oneshot::channel();
418
419 let start = SystemTime::now();
420
421 let operation = req.to_string();
422 let attributes = [KeyValue::new("operation", operation.clone())];
423
424 let capacity = self.tx.capacity();
425 STORAGE_CHANNEL_CAPACITY.record(capacity as u64, &attributes);
426 debug!(operation, capacity);
427
428 self.tx
429 .reserve()
430 .await
431 .map(|permit| permit.send((req, resp_tx)))
432 .inspect(|_| {
433 let permit_elapsed = self.elapsed_millis(start);
434 STORAGE_CHANNEL_PERMIT_DURATION.record(permit_elapsed, &attributes);
435 debug!(operation, permit_elapsed);
436 })
437 .inspect_err(|err| {
438 error!(operation, ?err);
439 STORAGE_CHANNEL_ERROR.add(1, &attributes);
440 })?;
441
442 resp_rx
443 .await
444 .map_err(|_| Error::OneshotRecv.into())
445 .inspect(|_| {
446 let elapsed_millis = self.elapsed_millis(start);
447 STORAGE_CHANNEL_REQUEST_DURATION.record(elapsed_millis, &attributes);
448 debug!(operation, elapsed_millis);
449 })
450 .inspect_err(|err| {
451 error!(operation, ?err);
452 STORAGE_CHANNEL_ERROR.add(1, &attributes);
453 })
454 }
455}
456
457static STORAGE_CHANNEL_REQUEST_DURATION: LazyLock<Histogram<u64>> = LazyLock::new(|| {
458 METER
459 .u64_histogram("nisshi_storage_channel_request_duration")
460 .with_unit("ms")
461 .with_description("Storage channel request latency in milliseconds")
462 .build()
463});
464
465static STORAGE_CHANNEL_PERMIT_DURATION: LazyLock<Histogram<u64>> = LazyLock::new(|| {
466 METER
467 .u64_histogram("nisshi_storage_channel_permit_duration")
468 .with_unit("ms")
469 .with_description("Storage channel permit latency in milliseconds")
470 .build()
471});
472
473static STORAGE_CHANNEL_ERROR: LazyLock<Counter<u64>> = LazyLock::new(|| {
474 METER
475 .u64_counter("nisshi_storage_channel_error")
476 .with_description("Storage channel error count")
477 .build()
478});
479
480#[async_trait]
481impl Storage for RequestChannelService {
482 #[instrument(skip_all)]
483 async fn register_broker(&self, broker_registration: BrokerRegistrationRequest) -> Result<()> {
484 self.serve(
485 Context::default(),
486 Request::RegisterBroker(broker_registration),
487 )
488 .await
489 .and_then(|response| {
490 if let Response::RegisterBroker(inner) = response {
491 inner.map_err(Into::into)
492 } else {
493 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
494 }
495 })
496 .map_err(Into::into)
497 }
498
499 #[instrument(skip_all)]
500 async fn incremental_alter_resource(
501 &self,
502 resource: AlterConfigsResource,
503 ) -> Result<AlterConfigsResourceResponse> {
504 self.serve(
505 Context::default(),
506 Request::IncrementalAlterResource(resource),
507 )
508 .await
509 .and_then(|response| {
510 if let Response::IncrementalAlterResponse(inner) = response {
511 inner.map_err(Into::into)
512 } else {
513 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
514 }
515 })
516 .map_err(Into::into)
517 }
518
519 #[instrument(skip_all)]
520 async fn create_topic(&self, topic: CreatableTopic, validate_only: bool) -> Result<Uuid> {
521 self.serve(
522 Context::default(),
523 Request::CreateTopic {
524 topic,
525 validate_only,
526 },
527 )
528 .await
529 .and_then(|response| {
530 if let Response::CreateTopic(inner) = response {
531 inner.map_err(Into::into)
532 } else {
533 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
534 }
535 })
536 .map_err(Into::into)
537 }
538
539 #[instrument(skip_all)]
540 async fn delete_records(
541 &self,
542 topics: &[DeleteRecordsTopic],
543 ) -> Result<Vec<DeleteRecordsTopicResult>> {
544 self.serve(
545 Context::default(),
546 Request::DeleteRecords(Vec::from(topics)),
547 )
548 .await
549 .and_then(|response| {
550 if let Response::DeleteRecords(inner) = response {
551 inner.map_err(Into::into)
552 } else {
553 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
554 }
555 })
556 .map_err(Into::into)
557 }
558
559 #[instrument(skip_all)]
560 async fn delete_topic(&self, topic: &TopicId) -> Result<ErrorCode> {
561 self.serve(Context::default(), Request::DeleteTopic(topic.to_owned()))
562 .await
563 .and_then(|response| {
564 if let Response::DeleteTopic(inner) = response {
565 inner.map_err(Into::into)
566 } else {
567 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
568 }
569 })
570 .map_err(Into::into)
571 }
572
573 #[instrument(skip_all)]
574 async fn brokers(&self) -> Result<Vec<DescribeClusterBroker>> {
575 self.serve(Context::default(), Request::Brokers)
576 .await
577 .and_then(|response| {
578 if let Response::Brokers(inner) = response {
579 inner.map_err(Into::into)
580 } else {
581 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
582 }
583 })
584 .map_err(Into::into)
585 }
586
587 #[instrument(skip_all)]
588 async fn produce(
589 &self,
590 transaction_id: Option<&str>,
591 topition: &Topition,
592 batch: deflated::Batch,
593 ) -> Result<i64> {
594 let transaction_id = transaction_id.map(|s| s.to_string());
595 let topition = topition.to_owned();
596
597 self.serve(
598 Context::default(),
599 Request::Produce {
600 transaction_id,
601 topition,
602 batch,
603 },
604 )
605 .await
606 .and_then(|response| {
607 if let Response::Produce(inner) = response {
608 inner.map_err(Into::into)
609 } else {
610 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
611 }
612 })
613 .map_err(Into::into)
614 }
615
616 #[instrument(skip_all)]
617 async fn fetch(
618 &self,
619 topition: &'_ Topition,
620 offset: i64,
621 min_bytes: u32,
622 max_bytes: u32,
623 isolation: IsolationLevel,
624 max_wait: Duration,
625 ) -> Result<Vec<deflated::Batch>> {
626 let topition = topition.to_owned();
627
628 self.serve(
629 Context::default(),
630 Request::Fetch {
631 topition,
632 offset,
633 min_bytes,
634 max_bytes,
635 isolation,
636 max_wait,
637 },
638 )
639 .await
640 .and_then(|response| {
641 if let Response::Fetch(inner) = response {
642 inner.map_err(Into::into)
643 } else {
644 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
645 }
646 })
647 .map_err(Into::into)
648 }
649
650 #[instrument(skip_all)]
651 async fn offset_stage(&self, topition: &Topition) -> Result<OffsetStage> {
652 self.serve(
653 Context::default(),
654 Request::OffsetStage(topition.to_owned()),
655 )
656 .await
657 .and_then(|response| {
658 if let Response::OffsetStage(inner) = response {
659 inner.map_err(Into::into)
660 } else {
661 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
662 }
663 })
664 .map_err(Into::into)
665 }
666
667 #[instrument(skip_all)]
668 async fn list_offsets(
669 &self,
670 isolation_level: IsolationLevel,
671 offsets: &[(Topition, ListOffset)],
672 ) -> Result<Vec<(Topition, ListOffsetResponse)>> {
673 let offsets = Vec::from(offsets);
674
675 self.serve(
676 Context::default(),
677 Request::ListOffsets {
678 isolation_level,
679 offsets,
680 },
681 )
682 .await
683 .and_then(|response| {
684 if let Response::ListOffsets(inner) = response {
685 inner.map_err(Into::into)
686 } else {
687 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
688 }
689 })
690 .map_err(Into::into)
691 }
692
693 #[instrument(skip_all)]
694 async fn offset_commit(
695 &self,
696 group_id: &str,
697 retention_time_ms: Option<Duration>,
698 offsets: &[(Topition, OffsetCommitRequest)],
699 ) -> Result<Vec<(Topition, ErrorCode)>> {
700 let group_id = group_id.to_string();
701 let offsets = Vec::from(offsets);
702
703 self.serve(
704 Context::default(),
705 Request::OffsetCommit {
706 group_id,
707 retention_time_ms,
708 offsets,
709 },
710 )
711 .await
712 .and_then(|response| {
713 if let Response::OffsetCommit(inner) = response {
714 inner.map_err(Into::into)
715 } else {
716 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
717 }
718 })
719 .map_err(Into::into)
720 }
721
722 #[instrument(skip_all)]
723 async fn committed_offset_topitions(&self, group_id: &str) -> Result<BTreeMap<Topition, i64>> {
724 let group_id = group_id.to_string();
725
726 self.serve(
727 Context::default(),
728 Request::CommittedOffsetTopitions(group_id),
729 )
730 .await
731 .and_then(|response| {
732 if let Response::CommittedOffsetTopitions(inner) = response {
733 inner.map_err(Into::into)
734 } else {
735 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
736 }
737 })
738 .map_err(Into::into)
739 }
740
741 #[instrument(skip_all)]
742 async fn offset_fetch(
743 &self,
744 group_id: Option<&str>,
745 topics: &[Topition],
746 require_stable: Option<bool>,
747 ) -> Result<BTreeMap<Topition, i64>> {
748 let group_id = group_id.map(|s| s.to_string());
749 let topics = Vec::from(topics);
750
751 self.serve(
752 Context::default(),
753 Request::OffsetFetch {
754 group_id,
755 topics,
756 require_stable,
757 },
758 )
759 .await
760 .and_then(|response| {
761 if let Response::OffsetFetch(inner) = response {
762 inner.map_err(Into::into)
763 } else {
764 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
765 }
766 })
767 .map_err(Into::into)
768 }
769
770 #[instrument(skip_all)]
771 async fn metadata(&self, topics: Option<&[TopicId]>) -> Result<MetadataResponse> {
772 let topics = topics.map(Vec::from);
773
774 self.serve(Context::default(), Request::Metadata(topics))
775 .await
776 .and_then(|response| {
777 if let Response::Metadata(inner) = response {
778 inner.map_err(Into::into)
779 } else {
780 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
781 }
782 })
783 .map_err(Into::into)
784 }
785
786 #[instrument(skip_all)]
787 async fn describe_config(
788 &self,
789 name: &str,
790 resource: ConfigResource,
791 keys: Option<&[String]>,
792 ) -> Result<DescribeConfigsResult> {
793 let name = name.to_string();
794 let keys = keys.map(Vec::from);
795
796 self.serve(
797 Context::default(),
798 Request::DescribeConfig {
799 name,
800 resource,
801 keys,
802 },
803 )
804 .await
805 .and_then(|response| {
806 if let Response::DescribeConfig(inner) = response {
807 inner.map_err(Into::into)
808 } else {
809 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
810 }
811 })
812 .map_err(Into::into)
813 }
814
815 #[instrument(skip_all)]
816 async fn describe_topic_partitions(
817 &self,
818 topics: Option<&[TopicId]>,
819 partition_limit: i32,
820 cursor: Option<Topition>,
821 ) -> Result<Vec<DescribeTopicPartitionsResponseTopic>> {
822 let topics = topics.map(Vec::from);
823
824 self.serve(
825 Context::default(),
826 Request::DescribeTopicPartitions {
827 topics,
828 partition_limit,
829 cursor,
830 },
831 )
832 .await
833 .and_then(|response| {
834 if let Response::DescribeTopicPartitions(inner) = response {
835 inner.map_err(Into::into)
836 } else {
837 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
838 }
839 })
840 .map_err(Into::into)
841 }
842
843 #[instrument(skip_all)]
844 async fn list_groups(&self, states_filter: Option<&[String]>) -> Result<Vec<ListedGroup>> {
845 let states_filter = states_filter.map(Vec::from);
846
847 self.serve(Context::default(), Request::ListGroups(states_filter))
848 .await
849 .and_then(|response| {
850 if let Response::ListGroups(inner) = response {
851 inner.map_err(Into::into)
852 } else {
853 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
854 }
855 })
856 .map_err(Into::into)
857 }
858
859 #[instrument(skip_all)]
860 async fn delete_groups(
861 &self,
862 group_ids: Option<&[String]>,
863 ) -> Result<Vec<DeletableGroupResult>> {
864 let group_ids = group_ids.map(Vec::from);
865
866 self.serve(Context::default(), Request::DeleteGroups(group_ids))
867 .await
868 .and_then(|response| {
869 if let Response::DeleteGroups(inner) = response {
870 inner.map_err(Into::into)
871 } else {
872 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
873 }
874 })
875 .map_err(Into::into)
876 }
877
878 #[instrument(skip_all)]
879 async fn describe_groups(
880 &self,
881 group_ids: Option<&[String]>,
882 include_authorized_operations: bool,
883 ) -> Result<Vec<NamedGroupDetail>> {
884 let group_ids = group_ids.map(Vec::from);
885
886 self.serve(
887 Context::default(),
888 Request::DescribeGroups {
889 group_ids,
890 include_authorized_operations,
891 },
892 )
893 .await
894 .and_then(|response| {
895 if let Response::DescribeGroups(inner) = response {
896 inner.map_err(Into::into)
897 } else {
898 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
899 }
900 })
901 .map_err(Into::into)
902 }
903
904 #[instrument(skip_all)]
905 async fn update_group(
906 &self,
907 group_id: &str,
908 detail: GroupDetail,
909 version: Option<Version>,
910 ) -> Result<Version, UpdateError<GroupDetail>> {
911 let group_id = group_id.to_string();
912
913 self.serve(
914 Context::default(),
915 Request::UpdateGroup {
916 group_id,
917 detail,
918 version,
919 },
920 )
921 .await
922 .and_then(|response| {
923 if let Response::UpdateGroup(inner) = response {
924 inner.map_err(Into::into)
925 } else {
926 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
927 }
928 })
929 .map_err(Into::into)
930 }
931
932 #[instrument(skip_all)]
933 async fn init_producer(
934 &self,
935 transaction_id: Option<&str>,
936 transaction_timeout_ms: i32,
937 producer_id: Option<i64>,
938 producer_epoch: Option<i16>,
939 ) -> Result<ProducerIdResponse> {
940 let transaction_id = transaction_id.map(|transaction_id| transaction_id.to_owned());
941
942 self.serve(
943 Context::default(),
944 Request::InitProducer {
945 transaction_id,
946 transaction_timeout_ms,
947 producer_id,
948 producer_epoch,
949 },
950 )
951 .await
952 .and_then(|response| {
953 if let Response::InitProducer(inner) = response {
954 inner.map_err(Into::into)
955 } else {
956 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
957 }
958 })
959 .map_err(Into::into)
960 }
961
962 #[instrument(skip_all)]
963 async fn txn_add_offsets(
964 &self,
965 transaction_id: &str,
966 producer_id: i64,
967 producer_epoch: i16,
968 group_id: &str,
969 ) -> Result<ErrorCode> {
970 let transaction_id = transaction_id.to_string();
971 let group_id = group_id.to_string();
972
973 self.serve(
974 Context::default(),
975 Request::TxnAddOffsets {
976 transaction_id,
977 producer_id,
978 producer_epoch,
979 group_id,
980 },
981 )
982 .await
983 .and_then(|response| {
984 if let Response::TxnAddOffsets(inner) = response {
985 inner.map_err(Into::into)
986 } else {
987 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
988 }
989 })
990 .map_err(Into::into)
991 }
992
993 #[instrument(skip_all)]
994 async fn txn_add_partitions(
995 &self,
996 partitions: TxnAddPartitionsRequest,
997 ) -> Result<TxnAddPartitionsResponse> {
998 self.serve(Context::default(), Request::TxnAddPartitions(partitions))
999 .await
1000 .and_then(|response| {
1001 if let Response::TxnAddPartitions(inner) = response {
1002 inner.map_err(Into::into)
1003 } else {
1004 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1005 }
1006 })
1007 .map_err(Into::into)
1008 }
1009
1010 #[instrument(skip_all)]
1011 async fn txn_offset_commit(
1012 &self,
1013 offsets: TxnOffsetCommitRequest,
1014 ) -> Result<Vec<TxnOffsetCommitResponseTopic>> {
1015 self.serve(Context::default(), Request::TxnOffsetCommit(offsets))
1016 .await
1017 .and_then(|response| {
1018 if let Response::TxnOffsetCommit(inner) = response {
1019 inner.map_err(Into::into)
1020 } else {
1021 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1022 }
1023 })
1024 .map_err(Into::into)
1025 }
1026
1027 #[instrument(skip_all)]
1028 async fn txn_end(
1029 &self,
1030 transaction_id: &str,
1031 producer_id: i64,
1032 producer_epoch: i16,
1033 committed: bool,
1034 ) -> Result<ErrorCode> {
1035 let transaction_id = transaction_id.to_string();
1036
1037 self.serve(
1038 Context::default(),
1039 Request::TxnEnd {
1040 transaction_id,
1041 producer_id,
1042 producer_epoch,
1043 committed,
1044 },
1045 )
1046 .await
1047 .and_then(|response| {
1048 if let Response::TxnEnd(inner) = response {
1049 inner.map_err(Into::into)
1050 } else {
1051 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1052 }
1053 })
1054 .map_err(Into::into)
1055 }
1056
1057 #[instrument(skip_all)]
1058 async fn maintain(&self, now: SystemTime) -> Result<()> {
1059 self.serve(Context::default(), Request::Maintain(now))
1060 .await
1061 .and_then(|response| {
1062 if let Response::Maintain(inner) = response {
1063 inner.map_err(Into::into)
1064 } else {
1065 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1066 }
1067 })
1068 .map_err(Into::into)
1069 }
1070
1071 #[instrument(skip_all)]
1072 async fn cluster_id(&self) -> Result<String> {
1073 self.serve(Context::default(), Request::ClusterId)
1074 .await
1075 .and_then(|response| {
1076 if let Response::ClusterId(inner) = response {
1077 inner.map_err(Into::into)
1078 } else {
1079 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1080 }
1081 })
1082 .map_err(Into::into)
1083 }
1084
1085 #[instrument(skip_all)]
1086 async fn node(&self) -> Result<i32> {
1087 self.serve(Context::default(), Request::Node)
1088 .await
1089 .and_then(|response| {
1090 if let Response::Node(inner) = response {
1091 inner.map_err(Into::into)
1092 } else {
1093 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1094 }
1095 })
1096 .map_err(Into::into)
1097 }
1098
1099 #[instrument(skip_all)]
1100 async fn advertised_listener(&self) -> Result<Url> {
1101 self.serve(Context::default(), Request::AdvertisedListener)
1102 .await
1103 .and_then(|response| {
1104 if let Response::AdvertisedListener(inner) = response {
1105 inner.map_err(Into::into)
1106 } else {
1107 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1108 }
1109 })
1110 .map_err(Into::into)
1111 }
1112
1113 #[instrument(skip_all)]
1114 async fn delete_user_scram_credential(
1115 &self,
1116 user: &str,
1117 mechanism: ScramMechanism,
1118 ) -> Result<()> {
1119 let user = user.to_string();
1120
1121 self.serve(
1122 Context::default(),
1123 Request::DeleteUserScramCredential { user, mechanism },
1124 )
1125 .await
1126 .and_then(|response| {
1127 if let Response::DeleteUserScramCredential(inner) = response {
1128 inner.map_err(Into::into)
1129 } else {
1130 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1131 }
1132 })
1133 .map_err(Into::into)
1134 }
1135
1136 #[instrument(skip_all)]
1137 async fn upsert_user_scram_credential(
1138 &self,
1139 user: &str,
1140 mechanism: ScramMechanism,
1141 credential: ScramCredential,
1142 ) -> Result<()> {
1143 let user = user.to_string();
1144
1145 self.serve(
1146 Context::default(),
1147 Request::UpsertUserScramCredential {
1148 user,
1149 mechanism,
1150 credential,
1151 },
1152 )
1153 .await
1154 .and_then(|response| {
1155 if let Response::UpsertUserScramCredential(inner) = response {
1156 inner.map_err(Into::into)
1157 } else {
1158 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1159 }
1160 })
1161 .map_err(Into::into)
1162 }
1163
1164 #[instrument(skip_all)]
1165 async fn user_scram_credential(
1166 &self,
1167 user: &str,
1168 mechanism: ScramMechanism,
1169 ) -> Result<Option<ScramCredential>> {
1170 let user = user.to_string();
1171
1172 self.serve(
1173 Context::default(),
1174 Request::UserScramCredential { user, mechanism },
1175 )
1176 .await
1177 .and_then(|response| {
1178 if let Response::UserScramCredential(inner) = response {
1179 inner.map_err(Into::into)
1180 } else {
1181 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1182 }
1183 })
1184 .map_err(Into::into)
1185 }
1186
1187 #[instrument(skip_all)]
1188 async fn ping(&self) -> Result<()> {
1189 self.serve(Context::default(), Request::Ping)
1190 .await
1191 .and_then(|response| {
1192 if let Response::Ping(inner) = response {
1193 inner.map_err(Into::into)
1194 } else {
1195 Err(Error::UnexpectedServiceResponse(Box::new(response)).into())
1196 }
1197 })
1198 .map_err(Into::into)
1199 }
1200}
1201
1202#[derive(Clone, Debug, Default)]
1203pub struct ChannelRequestLayer {
1204 cancellation: CancellationToken,
1205}
1206
1207impl ChannelRequestLayer {
1208 pub fn new(cancellation: CancellationToken) -> Self {
1209 Self { cancellation }
1210 }
1211}
1212
1213impl<S> Layer<S> for ChannelRequestLayer {
1214 type Service = ChannelRequestService<S>;
1215
1216 fn layer(&self, inner: S) -> Self::Service {
1217 Self::Service {
1218 inner,
1219 cancellation: self.cancellation.clone(),
1220 }
1221 }
1222}
1223
1224#[derive(Clone, Debug, Default)]
1225pub struct ChannelRequestService<S> {
1226 inner: S,
1227 cancellation: CancellationToken,
1228}
1229
1230impl<S, State> Service<State, RequestReceiver> for ChannelRequestService<S>
1231where
1232 S: Service<State, Request, Response = Response, Error = Error>,
1233 State: Clone + Send + Sync + 'static,
1234{
1235 type Response = ();
1236 type Error = Error;
1237
1238 async fn serve(
1239 &self,
1240 ctx: Context<State>,
1241 mut req: RequestReceiver,
1242 ) -> Result<Self::Response, Self::Error> {
1243 loop {
1244 tokio::select! {
1245 Some((request, tx)) = req.recv() => {
1246 self.inner
1247 .serve(ctx.clone(), request)
1248 .await
1249 .and_then(|response| {
1250 tx.send(response).map_err(|_unsent| Error::UnableToSend)
1251 })?
1252 }
1253
1254 cancelled = self.cancellation.cancelled() => {
1255 debug!(?cancelled);
1256 break;
1257 }
1258 }
1259 }
1260
1261 Ok(())
1262 }
1263}
1264
1265#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
1266pub struct RequestStorageService<G> {
1267 storage: G,
1268}
1269
1270impl<G> RequestStorageService<G>
1271where
1272 G: Storage,
1273{
1274 pub fn new(storage: G) -> Self {
1275 Self { storage }
1276 }
1277}
1278
1279impl<G, State> Service<State, Request> for RequestStorageService<G>
1280where
1281 G: Storage,
1282 State: Clone + Send + Sync + 'static,
1283{
1284 type Response = Response;
1285 type Error = Error;
1286
1287 async fn serve(
1288 &self,
1289 _ctx: Context<State>,
1290 req: Request,
1291 ) -> Result<Self::Response, Self::Error> {
1292 match req {
1293 Request::RegisterBroker(broker_registration) => Ok(Response::RegisterBroker(
1294 self.storage.register_broker(broker_registration).await,
1295 )),
1296 Request::IncrementalAlterResource(alter_configs_resource) => {
1297 Ok(Response::IncrementalAlterResponse(
1298 self.storage
1299 .incremental_alter_resource(alter_configs_resource)
1300 .await,
1301 ))
1302 }
1303 Request::CreateTopic {
1304 topic,
1305 validate_only,
1306 } => Ok(Response::CreateTopic(
1307 self.storage.create_topic(topic, validate_only).await,
1308 )),
1309 Request::DeleteRecords(delete_records_topics) => Ok(Response::DeleteRecords(
1310 self.storage
1311 .delete_records(&delete_records_topics[..])
1312 .await,
1313 )),
1314 Request::DeleteTopic(topic_id) => Ok(Response::DeleteTopic(
1315 self.storage.delete_topic(&topic_id).await,
1316 )),
1317 Request::Brokers => Ok(Response::Brokers(self.storage.brokers().await)),
1318 Request::Produce {
1319 transaction_id,
1320 topition,
1321 batch,
1322 } => Ok(Response::Produce(
1323 self.storage
1324 .produce(transaction_id.as_deref(), &topition, batch)
1325 .await,
1326 )),
1327 Request::Fetch {
1328 topition,
1329 offset,
1330 min_bytes,
1331 max_bytes,
1332 isolation,
1333 max_wait,
1334 } => Ok(Response::Fetch(
1335 self.storage
1336 .fetch(&topition, offset, min_bytes, max_bytes, isolation, max_wait)
1337 .await,
1338 )),
1339 Request::OffsetStage(topition) => Ok(Response::OffsetStage(
1340 self.storage.offset_stage(&topition).await,
1341 )),
1342 Request::ListOffsets {
1343 isolation_level,
1344 offsets,
1345 } => Ok(Response::ListOffsets(
1346 self.storage
1347 .list_offsets(isolation_level, &offsets[..])
1348 .await,
1349 )),
1350 Request::OffsetCommit {
1351 group_id,
1352 retention_time_ms,
1353 offsets,
1354 } => Ok(Response::OffsetCommit(
1355 self.storage
1356 .offset_commit(&group_id, retention_time_ms, &offsets[..])
1357 .await,
1358 )),
1359 Request::CommittedOffsetTopitions(group_id) => Ok(Response::CommittedOffsetTopitions(
1360 self.storage.committed_offset_topitions(&group_id).await,
1361 )),
1362 Request::OffsetFetch {
1363 group_id,
1364 topics,
1365 require_stable,
1366 } => Ok(Response::OffsetFetch(
1367 self.storage
1368 .offset_fetch(group_id.as_deref(), &topics[..], require_stable)
1369 .await,
1370 )),
1371 Request::Metadata(topic_ids) => Ok(Response::Metadata(
1372 self.storage.metadata(topic_ids.as_deref()).await,
1373 )),
1374 Request::DescribeConfig {
1375 name,
1376 resource,
1377 keys,
1378 } => Ok(Response::DescribeConfig(
1379 self.storage
1380 .describe_config(&name, resource, keys.as_deref())
1381 .await,
1382 )),
1383 Request::DescribeTopicPartitions {
1384 topics,
1385 partition_limit,
1386 cursor,
1387 } => Ok(Response::DescribeTopicPartitions(
1388 self.storage
1389 .describe_topic_partitions(topics.as_deref(), partition_limit, cursor)
1390 .await,
1391 )),
1392 Request::ListGroups(items) => Ok(Response::ListGroups(
1393 self.storage.list_groups(items.as_deref()).await,
1394 )),
1395 Request::DeleteGroups(items) => Ok(Response::DeleteGroups(
1396 self.storage.delete_groups(items.as_deref()).await,
1397 )),
1398 Request::DescribeGroups {
1399 group_ids,
1400 include_authorized_operations,
1401 } => Ok(Response::DescribeGroups(
1402 self.storage
1403 .describe_groups(group_ids.as_deref(), include_authorized_operations)
1404 .await,
1405 )),
1406 Request::UpdateGroup {
1407 group_id,
1408 detail,
1409 version,
1410 } => Ok(Response::UpdateGroup(
1411 self.storage.update_group(&group_id, detail, version).await,
1412 )),
1413 Request::InitProducer {
1414 transaction_id,
1415 transaction_timeout_ms,
1416 producer_id,
1417 producer_epoch,
1418 } => Ok(Response::InitProducer(
1419 self.storage
1420 .init_producer(
1421 transaction_id.as_deref(),
1422 transaction_timeout_ms,
1423 producer_id,
1424 producer_epoch,
1425 )
1426 .await,
1427 )),
1428 Request::TxnAddOffsets {
1429 transaction_id,
1430 producer_id,
1431 producer_epoch,
1432 group_id,
1433 } => Ok(Response::TxnAddOffsets(
1434 self.storage
1435 .txn_add_offsets(&transaction_id, producer_id, producer_epoch, &group_id)
1436 .await,
1437 )),
1438 Request::TxnAddPartitions(txn_add_partitions_request) => {
1439 Ok(Response::TxnAddPartitions(
1440 self.storage
1441 .txn_add_partitions(txn_add_partitions_request)
1442 .await,
1443 ))
1444 }
1445 Request::TxnOffsetCommit(txn_offset_commit_request) => Ok(Response::TxnOffsetCommit(
1446 self.storage
1447 .txn_offset_commit(txn_offset_commit_request)
1448 .await,
1449 )),
1450 Request::TxnEnd {
1451 transaction_id,
1452 producer_id,
1453 producer_epoch,
1454 committed,
1455 } => Ok(Response::TxnEnd(
1456 self.storage
1457 .txn_end(&transaction_id, producer_id, producer_epoch, committed)
1458 .await,
1459 )),
1460 Request::Maintain(now) => Ok(Response::Maintain(self.storage.maintain(now).await)),
1461 Request::ClusterId => Ok(Response::ClusterId(self.storage.cluster_id().await)),
1462 Request::Node => Ok(Response::Node(self.storage.node().await)),
1463 Request::AdvertisedListener => Ok(Response::AdvertisedListener(
1464 self.storage.advertised_listener().await,
1465 )),
1466 Request::DeleteUserScramCredential { user, mechanism } => {
1467 Ok(Response::DeleteUserScramCredential(
1468 self.storage
1469 .delete_user_scram_credential(&user[..], mechanism)
1470 .await,
1471 ))
1472 }
1473 Request::UpsertUserScramCredential {
1474 user,
1475 mechanism,
1476 credential,
1477 } => Ok(Response::UpsertUserScramCredential(
1478 self.storage
1479 .upsert_user_scram_credential(&user[..], mechanism, credential)
1480 .await,
1481 )),
1482 Request::UserScramCredential { user, mechanism } => Ok(Response::UserScramCredential(
1483 self.storage
1484 .user_scram_credential(&user[..], mechanism)
1485 .await,
1486 )),
1487 Request::Ping => Ok(Response::Ping(self.storage.ping().await)),
1488 }
1489 }
1490}