kubemq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +30 -0
- data/LICENSE +201 -0
- data/README.md +237 -0
- data/lib/kubemq/base_client.rb +180 -0
- data/lib/kubemq/cancellation_token.rb +63 -0
- data/lib/kubemq/channel_info.rb +84 -0
- data/lib/kubemq/configuration.rb +247 -0
- data/lib/kubemq/cq/client.rb +446 -0
- data/lib/kubemq/cq/command_message.rb +59 -0
- data/lib/kubemq/cq/command_received.rb +52 -0
- data/lib/kubemq/cq/command_response.rb +44 -0
- data/lib/kubemq/cq/command_response_message.rb +58 -0
- data/lib/kubemq/cq/commands_subscription.rb +45 -0
- data/lib/kubemq/cq/queries_subscription.rb +45 -0
- data/lib/kubemq/cq/query_message.rb +70 -0
- data/lib/kubemq/cq/query_received.rb +52 -0
- data/lib/kubemq/cq/query_response.rb +59 -0
- data/lib/kubemq/cq/query_response_message.rb +67 -0
- data/lib/kubemq/error_codes.rb +181 -0
- data/lib/kubemq/errors/error_mapper.rb +134 -0
- data/lib/kubemq/errors.rb +276 -0
- data/lib/kubemq/interceptors/auth_interceptor.rb +78 -0
- data/lib/kubemq/interceptors/error_mapping_interceptor.rb +75 -0
- data/lib/kubemq/interceptors/metrics_interceptor.rb +95 -0
- data/lib/kubemq/interceptors/retry_interceptor.rb +119 -0
- data/lib/kubemq/proto/kubemq_pb.rb +43 -0
- data/lib/kubemq/proto/kubemq_services_pb.rb +35 -0
- data/lib/kubemq/pubsub/client.rb +475 -0
- data/lib/kubemq/pubsub/event_message.rb +52 -0
- data/lib/kubemq/pubsub/event_received.rb +48 -0
- data/lib/kubemq/pubsub/event_send_result.rb +31 -0
- data/lib/kubemq/pubsub/event_sender.rb +112 -0
- data/lib/kubemq/pubsub/event_store_message.rb +53 -0
- data/lib/kubemq/pubsub/event_store_received.rb +47 -0
- data/lib/kubemq/pubsub/event_store_result.rb +33 -0
- data/lib/kubemq/pubsub/event_store_sender.rb +164 -0
- data/lib/kubemq/pubsub/events_store_subscription.rb +81 -0
- data/lib/kubemq/pubsub/events_subscription.rb +43 -0
- data/lib/kubemq/queues/client.rb +366 -0
- data/lib/kubemq/queues/downstream_receiver.rb +247 -0
- data/lib/kubemq/queues/queue_message.rb +99 -0
- data/lib/kubemq/queues/queue_message_received.rb +148 -0
- data/lib/kubemq/queues/queue_poll_request.rb +77 -0
- data/lib/kubemq/queues/queue_poll_response.rb +138 -0
- data/lib/kubemq/queues/queue_send_result.rb +49 -0
- data/lib/kubemq/queues/upstream_sender.rb +180 -0
- data/lib/kubemq/server_info.rb +57 -0
- data/lib/kubemq/subscription.rb +98 -0
- data/lib/kubemq/telemetry/otel.rb +64 -0
- data/lib/kubemq/telemetry/semconv.rb +51 -0
- data/lib/kubemq/transport/channel_manager.rb +212 -0
- data/lib/kubemq/transport/converter.rb +287 -0
- data/lib/kubemq/transport/grpc_transport.rb +411 -0
- data/lib/kubemq/transport/message_buffer.rb +105 -0
- data/lib/kubemq/transport/reconnect_manager.rb +111 -0
- data/lib/kubemq/transport/state_machine.rb +150 -0
- data/lib/kubemq/types.rb +80 -0
- data/lib/kubemq/validation/validator.rb +216 -0
- data/lib/kubemq/version.rb +6 -0
- data/lib/kubemq.rb +118 -0
- metadata +138 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: kubemq.proto
|
|
4
|
+
|
|
5
|
+
require 'google/protobuf'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
descriptor_data = "\n\x0ckubemq.proto\x12\x06kubemq\"a\n\nPingResult\x12\x0c\n\x04Host\x18\x01 \x01(\t\x12\x0f\n\x07Version\x18\x02 \x01(\t\x12\x17\n\x0fServerStartTime\x18\x03 \x01(\x03\x12\x1b\n\x13ServerUpTimeSeconds\x18\x04 \x01(\x03\"\x07\n\x05\x45mpty\"6\n\x06Result\x12\x0f\n\x07\x45ventID\x18\x01 \x01(\t\x12\x0c\n\x04Sent\x18\x02 \x01(\x08\x12\r\n\x05\x45rror\x18\x03 \x01(\t\"\xbe\x01\n\x05\x45vent\x12\x0f\n\x07\x45ventID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x03 \x01(\t\x12\x10\n\x08Metadata\x18\x04 \x01(\t\x12\x0c\n\x04\x42ody\x18\x05 \x01(\x0c\x12\r\n\x05Store\x18\x06 \x01(\x08\x12%\n\x04Tags\x18\x07 \x03(\x0b\x32\x17.kubemq.Event.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xd0\x01\n\x0c\x45ventReceive\x12\x0f\n\x07\x45ventID\x18\x01 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x02 \x01(\t\x12\x10\n\x08Metadata\x18\x03 \x01(\t\x12\x0c\n\x04\x42ody\x18\x04 \x01(\x0c\x12\x11\n\tTimestamp\x18\x05 \x01(\x03\x12\x10\n\x08Sequence\x18\x06 \x01(\x04\x12,\n\x04Tags\x18\x07 \x03(\x0b\x32\x1e.kubemq.EventReceive.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe3\x03\n\tSubscribe\x12:\n\x11SubscribeTypeData\x18\x01 \x01(\x0e\x32\x1f.kubemq.Subscribe.SubscribeType\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x03 \x01(\t\x12\r\n\x05Group\x18\x04 \x01(\t\x12>\n\x13\x45ventsStoreTypeData\x18\x05 \x01(\x0e\x32!.kubemq.Subscribe.EventsStoreType\x12\x1c\n\x14\x45ventsStoreTypeValue\x18\x06 \x01(\x03\"c\n\rSubscribeType\x12\x1a\n\x16SubscribeTypeUndefined\x10\x00\x12\n\n\x06\x45vents\x10\x01\x12\x0f\n\x0b\x45ventsStore\x10\x02\x12\x0c\n\x08\x43ommands\x10\x03\x12\x0b\n\x07Queries\x10\x04\"\xa4\x01\n\x0f\x45ventsStoreType\x12\x1c\n\x18\x45ventsStoreTypeUndefined\x10\x00\x12\x10\n\x0cStartNewOnly\x10\x01\x12\x12\n\x0eStartFromFirst\x10\x02\x12\x11\n\rStartFromLast\x10\x03\x12\x13\n\x0fStartAtSequence\x10\x04\x12\x0f\n\x0bStartAtTime\x10\x05\x12\x14\n\x10StartAtTimeDelta\x10\x06\"\x83\x03\n\x07Request\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x34\n\x0fRequestTypeData\x18\x02 \x01(\x0e\x32\x1b.kubemq.Request.RequestType\x12\x10\n\x08\x43lientID\x18\x03 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x04 \x01(\t\x12\x10\n\x08Metadata\x18\x05 \x01(\t\x12\x0c\n\x04\x42ody\x18\x06 \x01(\x0c\x12\x14\n\x0cReplyChannel\x18\x07 \x01(\t\x12\x0f\n\x07Timeout\x18\x08 \x01(\x05\x12\x10\n\x08\x43\x61\x63heKey\x18\t \x01(\t\x12\x10\n\x08\x43\x61\x63heTTL\x18\n \x01(\x05\x12\x0c\n\x04Span\x18\x0b \x01(\x0c\x12\'\n\x04Tags\x18\x0c \x03(\x0b\x32\x19.kubemq.Request.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"=\n\x0bRequestType\x12\x16\n\x12RequestTypeUnknown\x10\x00\x12\x0b\n\x07\x43ommand\x10\x01\x12\t\n\x05Query\x10\x02\"\x90\x02\n\x08Response\x12\x10\n\x08\x43lientID\x18\x01 \x01(\t\x12\x11\n\tRequestID\x18\x02 \x01(\t\x12\x14\n\x0cReplyChannel\x18\x03 \x01(\t\x12\x10\n\x08Metadata\x18\x04 \x01(\t\x12\x0c\n\x04\x42ody\x18\x05 \x01(\x0c\x12\x10\n\x08\x43\x61\x63heHit\x18\x06 \x01(\x08\x12\x11\n\tTimestamp\x18\x07 \x01(\x03\x12\x10\n\x08\x45xecuted\x18\x08 \x01(\x08\x12\r\n\x05\x45rror\x18\t \x01(\t\x12\x0c\n\x04Span\x18\n \x01(\x0c\x12(\n\x04Tags\x18\x0b \x03(\x0b\x32\x1a.kubemq.Response.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9f\x02\n\x0cQueueMessage\x12\x11\n\tMessageID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x03 \x01(\t\x12\x10\n\x08Metadata\x18\x04 \x01(\t\x12\x0c\n\x04\x42ody\x18\x05 \x01(\x0c\x12,\n\x04Tags\x18\x06 \x03(\x0b\x32\x1e.kubemq.QueueMessage.TagsEntry\x12\x32\n\nAttributes\x18\x07 \x01(\x0b\x32\x1e.kubemq.QueueMessageAttributes\x12*\n\x06Policy\x18\x08 \x01(\x0b\x32\x1a.kubemq.QueueMessagePolicy\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"T\n\x19QueueMessagesBatchRequest\x12\x0f\n\x07\x42\x61tchID\x18\x01 \x01(\t\x12&\n\x08Messages\x18\x02 \x03(\x0b\x32\x14.kubemq.QueueMessage\"r\n\x1aQueueMessagesBatchResponse\x12\x0f\n\x07\x42\x61tchID\x18\x01 \x01(\t\x12/\n\x07Results\x18\x02 \x03(\x0b\x32\x1e.kubemq.SendQueueMessageResult\x12\x12\n\nHaveErrors\x18\x03 \x01(\x08\"\xbc\x01\n\x16QueueMessageAttributes\x12\x11\n\tTimestamp\x18\x01 \x01(\x03\x12\x10\n\x08Sequence\x18\x02 \x01(\x04\x12\x11\n\tMD5OfBody\x18\x03 \x01(\t\x12\x14\n\x0cReceiveCount\x18\x04 \x01(\x05\x12\x10\n\x08ReRouted\x18\x05 \x01(\x08\x12\x19\n\x11ReRoutedFromQueue\x18\x06 \x01(\t\x12\x14\n\x0c\x45xpirationAt\x18\x07 \x01(\x03\x12\x11\n\tDelayedTo\x18\x08 \x01(\x03\"w\n\x12QueueMessagePolicy\x12\x19\n\x11\x45xpirationSeconds\x18\x01 \x01(\x05\x12\x14\n\x0c\x44\x65laySeconds\x18\x02 \x01(\x05\x12\x17\n\x0fMaxReceiveCount\x18\x03 \x01(\x05\x12\x17\n\x0fMaxReceiveQueue\x18\x04 \x01(\t\"\x84\x01\n\x16SendQueueMessageResult\x12\x11\n\tMessageID\x18\x01 \x01(\t\x12\x0e\n\x06SentAt\x18\x02 \x01(\x03\x12\x14\n\x0c\x45xpirationAt\x18\x03 \x01(\x03\x12\x11\n\tDelayedTo\x18\x04 \x01(\x03\x12\x0f\n\x07IsError\x18\x05 \x01(\x08\x12\r\n\x05\x45rror\x18\x06 \x01(\t\"\x99\x01\n\x1bReceiveQueueMessagesRequest\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x03 \x01(\t\x12\x1b\n\x13MaxNumberOfMessages\x18\x04 \x01(\x05\x12\x17\n\x0fWaitTimeSeconds\x18\x05 \x01(\x05\x12\x0e\n\x06IsPeak\x18\x06 \x01(\x08\"\xbc\x01\n\x1cReceiveQueueMessagesResponse\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12&\n\x08Messages\x18\x02 \x03(\x0b\x32\x14.kubemq.QueueMessage\x12\x18\n\x10MessagesReceived\x18\x03 \x01(\x05\x12\x17\n\x0fMessagesExpired\x18\x04 \x01(\x05\x12\x0e\n\x06IsPeak\x18\x05 \x01(\x08\x12\x0f\n\x07IsError\x18\x06 \x01(\x08\x12\r\n\x05\x45rror\x18\x07 \x01(\t\"k\n\x1a\x41\x63kAllQueueMessagesRequest\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x0f\n\x07\x43hannel\x18\x03 \x01(\t\x12\x17\n\x0fWaitTimeSeconds\x18\x04 \x01(\x05\"j\n\x1b\x41\x63kAllQueueMessagesResponse\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x18\n\x10\x41\x66\x66\x65\x63tedMessages\x18\x02 \x01(\x04\x12\x0f\n\x07IsError\x18\x03 \x01(\x08\x12\r\n\x05\x45rror\x18\x04 \x01(\t\"\x84\x02\n\x1aStreamQueueMessagesRequest\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12\x38\n\x15StreamRequestTypeData\x18\x03 \x01(\x0e\x32\x19.kubemq.StreamRequestType\x12\x0f\n\x07\x43hannel\x18\x04 \x01(\t\x12\x19\n\x11VisibilitySeconds\x18\x05 \x01(\x05\x12\x17\n\x0fWaitTimeSeconds\x18\x06 \x01(\x05\x12\x13\n\x0bRefSequence\x18\x07 \x01(\x04\x12-\n\x0fModifiedMessage\x18\x08 \x01(\x0b\x32\x14.kubemq.QueueMessage\"\xb1\x01\n\x1bStreamQueueMessagesResponse\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x38\n\x15StreamRequestTypeData\x18\x02 \x01(\x0e\x32\x19.kubemq.StreamRequestType\x12%\n\x07Message\x18\x03 \x01(\x0b\x32\x14.kubemq.QueueMessage\x12\x0f\n\x07IsError\x18\x04 \x01(\x08\x12\r\n\x05\x45rror\x18\x05 \x01(\t\"R\n\x15QueuesUpstreamRequest\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12&\n\x08Messages\x18\x02 \x03(\x0b\x32\x14.kubemq.QueueMessage\"\x7f\n\x16QueuesUpstreamResponse\x12\x14\n\x0cRefRequestID\x18\x01 \x01(\t\x12/\n\x07Results\x18\x02 \x03(\x0b\x32\x1e.kubemq.SendQueueMessageResult\x12\x0f\n\x07IsError\x18\x03 \x01(\x08\x12\r\n\x05\x45rror\x18\x04 \x01(\t\"\x80\x03\n\x17QueuesDownstreamRequest\x12\x11\n\tRequestID\x18\x01 \x01(\t\x12\x10\n\x08\x43lientID\x18\x02 \x01(\t\x12<\n\x0fRequestTypeData\x18\x03 \x01(\x0e\x32#.kubemq.QueuesDownstreamRequestType\x12\x0f\n\x07\x43hannel\x18\x04 \x01(\t\x12\x10\n\x08MaxItems\x18\x05 \x01(\x05\x12\x13\n\x0bWaitTimeout\x18\x06 \x01(\x05\x12\x0f\n\x07\x41utoAck\x18\x07 \x01(\x08\x12\x16\n\x0eReQueueChannel\x18\x08 \x01(\t\x12\x15\n\rSequenceRange\x18\t \x03(\x03\x12\x18\n\x10RefTransactionId\x18\n \x01(\t\x12?\n\x08Metadata\x18\x0c \x03(\x0b\x32-.kubemq.QueuesDownstreamRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf4\x02\n\x18QueuesDownstreamResponse\x12\x15\n\rTransactionId\x18\x01 \x01(\t\x12\x14\n\x0cRefRequestId\x18\x02 \x01(\t\x12<\n\x0fRequestTypeData\x18\x03 \x01(\x0e\x32#.kubemq.QueuesDownstreamRequestType\x12&\n\x08Messages\x18\x04 \x03(\x0b\x32\x14.kubemq.QueueMessage\x12\x15\n\rActiveOffsets\x18\x05 \x03(\x03\x12\x0f\n\x07IsError\x18\x06 \x01(\x08\x12\r\n\x05\x45rror\x18\x07 \x01(\t\x12\x1b\n\x13TransactionComplete\x18\x08 \x01(\x08\x12@\n\x08Metadata\x18\t \x03(\x0b\x32..kubemq.QueuesDownstreamResponse.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01*\xaa\x01\n\x11StreamRequestType\x12\x1c\n\x18StreamRequestTypeUnknown\x10\x00\x12\x12\n\x0eReceiveMessage\x10\x01\x12\x0e\n\nAckMessage\x10\x02\x12\x11\n\rRejectMessage\x10\x03\x12\x14\n\x10ModifyVisibility\x10\x04\x12\x11\n\rResendMessage\x10\x05\x12\x17\n\x13SendModifiedMessage\x10\x06*\xea\x01\n\x1bQueuesDownstreamRequestType\x12\x1a\n\x16PollRequestTypeUnknown\x10\x00\x12\x07\n\x03Get\x10\x01\x12\n\n\x06\x41\x63kAll\x10\x02\x12\x0c\n\x08\x41\x63kRange\x10\x03\x12\x0b\n\x07NAckAll\x10\x04\x12\r\n\tNAckRange\x10\x05\x12\x0e\n\nReQueueAll\x10\x06\x12\x10\n\x0cReQueueRange\x10\x07\x12\x11\n\rActiveOffsets\x10\x08\x12\x15\n\x11TransactionStatus\x10\t\x12\x11\n\rCloseByClient\x10\n\x12\x11\n\rCloseByServer\x10\x0b\x32\x93\x08\n\x06kubemq\x12,\n\tSendEvent\x12\r.kubemq.Event\x1a\x0e.kubemq.Result\"\x00\x12\x37\n\x10SendEventsStream\x12\r.kubemq.Event\x1a\x0e.kubemq.Result\"\x00(\x01\x30\x01\x12@\n\x11SubscribeToEvents\x12\x11.kubemq.Subscribe\x1a\x14.kubemq.EventReceive\"\x00\x30\x01\x12=\n\x13SubscribeToRequests\x12\x11.kubemq.Subscribe\x1a\x0f.kubemq.Request\"\x00\x30\x01\x12\x32\n\x0bSendRequest\x12\x0f.kubemq.Request\x1a\x10.kubemq.Response\"\x00\x12\x31\n\x0cSendResponse\x12\x10.kubemq.Response\x1a\r.kubemq.Empty\"\x00\x12J\n\x10SendQueueMessage\x12\x14.kubemq.QueueMessage\x1a\x1e.kubemq.SendQueueMessageResult\"\x00\x12\x61\n\x16SendQueueMessagesBatch\x12!.kubemq.QueueMessagesBatchRequest\x1a\".kubemq.QueueMessagesBatchResponse\"\x00\x12\x63\n\x14ReceiveQueueMessages\x12#.kubemq.ReceiveQueueMessagesRequest\x1a$.kubemq.ReceiveQueueMessagesResponse\"\x00\x12\x63\n\x12StreamQueueMessage\x12\".kubemq.StreamQueueMessagesRequest\x1a#.kubemq.StreamQueueMessagesResponse\"\x00(\x01\x30\x01\x12`\n\x13\x41\x63kAllQueueMessages\x12\".kubemq.AckAllQueueMessagesRequest\x1a#.kubemq.AckAllQueueMessagesResponse\"\x00\x12+\n\x04Ping\x12\r.kubemq.Empty\x1a\x12.kubemq.PingResult\"\x00\x12[\n\x10QueuesDownstream\x12\x1f.kubemq.QueuesDownstreamRequest\x1a .kubemq.QueuesDownstreamResponse\"\x00(\x01\x30\x01\x12U\n\x0eQueuesUpstream\x12\x1d.kubemq.QueuesUpstreamRequest\x1a\x1e.kubemq.QueuesUpstreamResponse\"\x00(\x01\x30\x01\x62\x06proto3"
|
|
9
|
+
|
|
10
|
+
pool = ::Google::Protobuf::DescriptorPool.generated_pool
|
|
11
|
+
pool.add_serialized_file(descriptor_data)
|
|
12
|
+
|
|
13
|
+
module Kubemq
|
|
14
|
+
PingResult = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.PingResult").msgclass
|
|
15
|
+
Empty = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Empty").msgclass
|
|
16
|
+
Result = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Result").msgclass
|
|
17
|
+
Event = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Event").msgclass
|
|
18
|
+
EventReceive = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.EventReceive").msgclass
|
|
19
|
+
Subscribe = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Subscribe").msgclass
|
|
20
|
+
Subscribe::SubscribeType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Subscribe.SubscribeType").enummodule
|
|
21
|
+
Subscribe::EventsStoreType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Subscribe.EventsStoreType").enummodule
|
|
22
|
+
Request = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Request").msgclass
|
|
23
|
+
Request::RequestType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Request.RequestType").enummodule
|
|
24
|
+
Response = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.Response").msgclass
|
|
25
|
+
QueueMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueueMessage").msgclass
|
|
26
|
+
QueueMessagesBatchRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueueMessagesBatchRequest").msgclass
|
|
27
|
+
QueueMessagesBatchResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueueMessagesBatchResponse").msgclass
|
|
28
|
+
QueueMessageAttributes = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueueMessageAttributes").msgclass
|
|
29
|
+
QueueMessagePolicy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueueMessagePolicy").msgclass
|
|
30
|
+
SendQueueMessageResult = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.SendQueueMessageResult").msgclass
|
|
31
|
+
ReceiveQueueMessagesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.ReceiveQueueMessagesRequest").msgclass
|
|
32
|
+
ReceiveQueueMessagesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.ReceiveQueueMessagesResponse").msgclass
|
|
33
|
+
AckAllQueueMessagesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.AckAllQueueMessagesRequest").msgclass
|
|
34
|
+
AckAllQueueMessagesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.AckAllQueueMessagesResponse").msgclass
|
|
35
|
+
StreamQueueMessagesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.StreamQueueMessagesRequest").msgclass
|
|
36
|
+
StreamQueueMessagesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.StreamQueueMessagesResponse").msgclass
|
|
37
|
+
QueuesUpstreamRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueuesUpstreamRequest").msgclass
|
|
38
|
+
QueuesUpstreamResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueuesUpstreamResponse").msgclass
|
|
39
|
+
QueuesDownstreamRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueuesDownstreamRequest").msgclass
|
|
40
|
+
QueuesDownstreamResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueuesDownstreamResponse").msgclass
|
|
41
|
+
StreamRequestType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.StreamRequestType").enummodule
|
|
42
|
+
QueuesDownstreamRequestType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("kubemq.QueuesDownstreamRequestType").enummodule
|
|
43
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
2
|
+
# Source: kubemq.proto for package 'kubemq'
|
|
3
|
+
|
|
4
|
+
require 'grpc'
|
|
5
|
+
require_relative 'kubemq_pb'
|
|
6
|
+
|
|
7
|
+
module Kubemq
|
|
8
|
+
module Kubemq
|
|
9
|
+
class Service
|
|
10
|
+
|
|
11
|
+
include ::GRPC::GenericService
|
|
12
|
+
|
|
13
|
+
self.marshal_class_method = :encode
|
|
14
|
+
self.unmarshal_class_method = :decode
|
|
15
|
+
self.service_name = 'kubemq.kubemq'
|
|
16
|
+
|
|
17
|
+
rpc :SendEvent, ::Kubemq::Event, ::Kubemq::Result
|
|
18
|
+
rpc :SendEventsStream, stream(::Kubemq::Event), stream(::Kubemq::Result)
|
|
19
|
+
rpc :SubscribeToEvents, ::Kubemq::Subscribe, stream(::Kubemq::EventReceive)
|
|
20
|
+
rpc :SubscribeToRequests, ::Kubemq::Subscribe, stream(::Kubemq::Request)
|
|
21
|
+
rpc :SendRequest, ::Kubemq::Request, ::Kubemq::Response
|
|
22
|
+
rpc :SendResponse, ::Kubemq::Response, ::Kubemq::Empty
|
|
23
|
+
rpc :SendQueueMessage, ::Kubemq::QueueMessage, ::Kubemq::SendQueueMessageResult
|
|
24
|
+
rpc :SendQueueMessagesBatch, ::Kubemq::QueueMessagesBatchRequest, ::Kubemq::QueueMessagesBatchResponse
|
|
25
|
+
rpc :ReceiveQueueMessages, ::Kubemq::ReceiveQueueMessagesRequest, ::Kubemq::ReceiveQueueMessagesResponse
|
|
26
|
+
rpc :StreamQueueMessage, stream(::Kubemq::StreamQueueMessagesRequest), stream(::Kubemq::StreamQueueMessagesResponse)
|
|
27
|
+
rpc :AckAllQueueMessages, ::Kubemq::AckAllQueueMessagesRequest, ::Kubemq::AckAllQueueMessagesResponse
|
|
28
|
+
rpc :Ping, ::Kubemq::Empty, ::Kubemq::PingResult
|
|
29
|
+
rpc :QueuesDownstream, stream(::Kubemq::QueuesDownstreamRequest), stream(::Kubemq::QueuesDownstreamResponse)
|
|
30
|
+
rpc :QueuesUpstream, stream(::Kubemq::QueuesUpstreamRequest), stream(::Kubemq::QueuesUpstreamResponse)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Stub = Service.rpc_stub_class
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module KubeMQ
|
|
6
|
+
# Client for KubeMQ pub/sub messaging — events (fire-and-forget) and
|
|
7
|
+
# events store (durable with replay).
|
|
8
|
+
#
|
|
9
|
+
# Inherits connection management and channel CRUD from {BaseClient}.
|
|
10
|
+
# Subscription methods run on background threads and accept a block for
|
|
11
|
+
# incoming messages.
|
|
12
|
+
#
|
|
13
|
+
# @example Send and subscribe to events
|
|
14
|
+
# client = KubeMQ::PubSubClient.new(address: "localhost:50000")
|
|
15
|
+
#
|
|
16
|
+
# token = KubeMQ::CancellationToken.new
|
|
17
|
+
# sub = KubeMQ::PubSub::EventsSubscription.new(channel: "notifications")
|
|
18
|
+
# client.subscribe_to_events(sub, cancellation_token: token) do |event|
|
|
19
|
+
# puts "Received: #{event.body}"
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# client.send_event(
|
|
23
|
+
# KubeMQ::PubSub::EventMessage.new(channel: "notifications", body: "hello")
|
|
24
|
+
# )
|
|
25
|
+
# sleep 1
|
|
26
|
+
# token.cancel
|
|
27
|
+
# client.close
|
|
28
|
+
#
|
|
29
|
+
# @see PubSub::EventMessage
|
|
30
|
+
# @see PubSub::EventsSubscription
|
|
31
|
+
# @see PubSub::EventsStoreSubscription
|
|
32
|
+
class PubSubClient < BaseClient
|
|
33
|
+
def initialize(**kwargs)
|
|
34
|
+
super
|
|
35
|
+
@senders = []
|
|
36
|
+
@senders_mutex = Mutex.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Sends a single event to a channel. Fire-and-forget semantics — the
|
|
40
|
+
# server does not guarantee persistence.
|
|
41
|
+
#
|
|
42
|
+
# @param message [PubSub::EventMessage] event to send
|
|
43
|
+
#
|
|
44
|
+
# @return [PubSub::EventSendResult] send confirmation with error status
|
|
45
|
+
#
|
|
46
|
+
# @raise [ValidationError] if channel name or content is invalid
|
|
47
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
48
|
+
# @raise [ConnectionError] if unable to reach the broker
|
|
49
|
+
#
|
|
50
|
+
# @example
|
|
51
|
+
# msg = KubeMQ::PubSub::EventMessage.new(
|
|
52
|
+
# channel: "events.orders",
|
|
53
|
+
# metadata: "order-created",
|
|
54
|
+
# body: '{"id": 42}',
|
|
55
|
+
# tags: { "env" => "production" }
|
|
56
|
+
# )
|
|
57
|
+
# result = client.send_event(msg)
|
|
58
|
+
# puts "Sent: #{result.id}" if result.sent
|
|
59
|
+
#
|
|
60
|
+
# @see PubSub::EventMessage
|
|
61
|
+
# @see PubSub::EventSendResult
|
|
62
|
+
def send_event(message)
|
|
63
|
+
Validator.validate_channel!(message.channel, allow_wildcards: false)
|
|
64
|
+
Validator.validate_content!(message.metadata, message.body)
|
|
65
|
+
ensure_connected!
|
|
66
|
+
|
|
67
|
+
proto = Transport::Converter.event_to_proto(message, @config.client_id, store: false)
|
|
68
|
+
result = @transport.kubemq_client.send_event(proto)
|
|
69
|
+
hash = Transport::Converter.proto_to_event_result(result)
|
|
70
|
+
PubSub::EventSendResult.new(**hash)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Creates a streaming event sender for high-throughput publishing.
|
|
74
|
+
#
|
|
75
|
+
# The sender holds a persistent gRPC stream open. Close it explicitly
|
|
76
|
+
# when finished, or it will be closed when the client is closed.
|
|
77
|
+
#
|
|
78
|
+
# @param on_error [Proc, nil] callback receiving {Error} on stream failures
|
|
79
|
+
#
|
|
80
|
+
# @return [PubSub::EventSender] streaming sender bound to this client
|
|
81
|
+
#
|
|
82
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
83
|
+
# @raise [ConnectionError] if unable to reach the broker
|
|
84
|
+
#
|
|
85
|
+
# @see PubSub::EventSender
|
|
86
|
+
def create_events_sender(on_error: nil)
|
|
87
|
+
ensure_connected!
|
|
88
|
+
sender = PubSub::EventSender.new(transport: @transport, client_id: @config.client_id, on_error: on_error)
|
|
89
|
+
senders_mutex.synchronize { senders_list << sender }
|
|
90
|
+
sender
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Subscribes to real-time events on a channel. Runs on a background
|
|
94
|
+
# thread; incoming events are delivered to the provided block.
|
|
95
|
+
#
|
|
96
|
+
# The subscription automatically reconnects with exponential backoff on
|
|
97
|
+
# transient failures. Use the returned {Subscription} or the
|
|
98
|
+
# +cancellation_token+ to stop.
|
|
99
|
+
#
|
|
100
|
+
# @param subscription [PubSub::EventsSubscription] channel and group config
|
|
101
|
+
# @param cancellation_token [CancellationToken, nil] token for cooperative
|
|
102
|
+
# cancellation (auto-created if nil)
|
|
103
|
+
# @param on_error [Proc, nil] callback receiving {Error} on stream or
|
|
104
|
+
# callback failures
|
|
105
|
+
# @yield [event] called for each received event on a background thread
|
|
106
|
+
# @yieldparam event [PubSub::EventReceived] the received event
|
|
107
|
+
#
|
|
108
|
+
# @return [Subscription] handle to check status, cancel, or join
|
|
109
|
+
#
|
|
110
|
+
# @raise [ArgumentError] if no block is given
|
|
111
|
+
# @raise [ValidationError] if the subscription channel is invalid
|
|
112
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
113
|
+
#
|
|
114
|
+
# @note Wildcard channels (containing +*+ or +>+) are supported for events.
|
|
115
|
+
#
|
|
116
|
+
# @example
|
|
117
|
+
# token = KubeMQ::CancellationToken.new
|
|
118
|
+
# sub_config = KubeMQ::PubSub::EventsSubscription.new(
|
|
119
|
+
# channel: "events.>", group: "workers"
|
|
120
|
+
# )
|
|
121
|
+
# subscription = client.subscribe_to_events(sub_config, cancellation_token: token) do |event|
|
|
122
|
+
# puts "#{event.channel}: #{event.body}"
|
|
123
|
+
# end
|
|
124
|
+
# # later...
|
|
125
|
+
# token.cancel
|
|
126
|
+
# subscription.wait(5)
|
|
127
|
+
#
|
|
128
|
+
# @see PubSub::EventsSubscription
|
|
129
|
+
# @see PubSub::EventReceived
|
|
130
|
+
# @see CancellationToken
|
|
131
|
+
# @see Subscription
|
|
132
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength -- subscription with auto-reconnect loop
|
|
133
|
+
def subscribe_to_events(subscription, cancellation_token: nil, on_error: nil, &block)
|
|
134
|
+
raise ArgumentError, 'Block required for subscribe_to_events' unless block
|
|
135
|
+
|
|
136
|
+
Validator.validate_channel!(subscription.channel, allow_wildcards: true)
|
|
137
|
+
ensure_connected!
|
|
138
|
+
|
|
139
|
+
cancellation_token ||= CancellationToken.new
|
|
140
|
+
nil
|
|
141
|
+
|
|
142
|
+
thread = Thread.new do
|
|
143
|
+
@transport.register_subscription(Thread.current)
|
|
144
|
+
Thread.current[:cancellation_token] = cancellation_token
|
|
145
|
+
reconnect_attempts = 0
|
|
146
|
+
begin
|
|
147
|
+
loop do
|
|
148
|
+
break if cancellation_token.cancelled?
|
|
149
|
+
|
|
150
|
+
begin
|
|
151
|
+
@transport.ensure_connected!
|
|
152
|
+
proto_sub = Transport::Converter.subscribe_to_proto(subscription, @config.client_id)
|
|
153
|
+
stream = @transport.kubemq_client.subscribe_to_events(proto_sub)
|
|
154
|
+
Thread.current[:grpc_call] = stream
|
|
155
|
+
reconnect_attempts = 0
|
|
156
|
+
stream.each do |event_receive|
|
|
157
|
+
break if cancellation_token.cancelled?
|
|
158
|
+
|
|
159
|
+
hash = Transport::Converter.proto_to_event_received(event_receive)
|
|
160
|
+
received = PubSub::EventReceived.new(**hash)
|
|
161
|
+
begin
|
|
162
|
+
block.call(received)
|
|
163
|
+
rescue StandardError => e
|
|
164
|
+
begin
|
|
165
|
+
on_error&.call(Error.new("Callback error: #{e.message}", code: ErrorCode::CALLBACK_ERROR))
|
|
166
|
+
rescue StandardError => nested
|
|
167
|
+
Kernel.warn("[kubemq] on_error callback raised: #{nested.message}")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
rescue CancellationError
|
|
172
|
+
break
|
|
173
|
+
rescue GRPC::BadStatus, StandardError => e
|
|
174
|
+
@transport.on_disconnect! if e.is_a?(GRPC::Unavailable) || e.is_a?(GRPC::DeadlineExceeded)
|
|
175
|
+
reconnect_attempts += 1
|
|
176
|
+
begin
|
|
177
|
+
if e.is_a?(GRPC::BadStatus)
|
|
178
|
+
on_error&.call(ErrorMapper.map_grpc_error(e, operation: 'subscribe_events'))
|
|
179
|
+
else
|
|
180
|
+
on_error&.call(Error.new(e.message, code: ErrorCode::STREAM_BROKEN))
|
|
181
|
+
end
|
|
182
|
+
rescue StandardError => cb_err
|
|
183
|
+
Kernel.warn("[kubemq] on_error callback raised: #{cb_err.message}")
|
|
184
|
+
end
|
|
185
|
+
delay = [@config.reconnect_policy.base_interval *
|
|
186
|
+
(@config.reconnect_policy.multiplier**(reconnect_attempts - 1)),
|
|
187
|
+
@config.reconnect_policy.max_delay].min
|
|
188
|
+
sleep(delay) unless cancellation_token.cancelled?
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
rescue StandardError => e
|
|
192
|
+
Thread.current[:kubemq_subscription]&.mark_error(e)
|
|
193
|
+
ensure
|
|
194
|
+
begin; Thread.current[:grpc_call]&.cancel; rescue StandardError; end
|
|
195
|
+
Thread.current[:kubemq_subscription]&.mark_closed
|
|
196
|
+
@transport.unregister_subscription(Thread.current)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
sub_wrapper = KubeMQ::Subscription.new(thread: thread, cancellation_token: cancellation_token)
|
|
201
|
+
thread[:kubemq_subscription] = sub_wrapper
|
|
202
|
+
sub_wrapper
|
|
203
|
+
end
|
|
204
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
205
|
+
|
|
206
|
+
# Sends a single event to a durable events store channel.
|
|
207
|
+
#
|
|
208
|
+
# Unlike {#send_event}, the broker persists events store messages and
|
|
209
|
+
# supports replay via {#subscribe_to_events_store}.
|
|
210
|
+
#
|
|
211
|
+
# @param message [PubSub::EventStoreMessage] event store message to send
|
|
212
|
+
#
|
|
213
|
+
# @return [PubSub::EventStoreResult] send confirmation with error status
|
|
214
|
+
#
|
|
215
|
+
# @raise [ValidationError] if channel name or content is invalid
|
|
216
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
217
|
+
# @raise [ConnectionError] if unable to reach the broker
|
|
218
|
+
#
|
|
219
|
+
# @example
|
|
220
|
+
# msg = KubeMQ::PubSub::EventStoreMessage.new(
|
|
221
|
+
# channel: "orders.created",
|
|
222
|
+
# body: '{"id": 42}'
|
|
223
|
+
# )
|
|
224
|
+
# result = client.send_event_store(msg)
|
|
225
|
+
# puts "Stored: #{result.id}" if result.sent
|
|
226
|
+
#
|
|
227
|
+
# @see PubSub::EventStoreMessage
|
|
228
|
+
# @see PubSub::EventStoreResult
|
|
229
|
+
def send_event_store(message)
|
|
230
|
+
Validator.validate_channel!(message.channel, allow_wildcards: false)
|
|
231
|
+
Validator.validate_content!(message.metadata, message.body)
|
|
232
|
+
ensure_connected!
|
|
233
|
+
|
|
234
|
+
proto = Transport::Converter.event_to_proto(message, @config.client_id, store: true)
|
|
235
|
+
result = @transport.kubemq_client.send_event(proto)
|
|
236
|
+
hash = Transport::Converter.proto_to_event_result(result)
|
|
237
|
+
PubSub::EventStoreResult.new(**hash)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Creates a streaming events store sender for high-throughput publishing
|
|
241
|
+
# with synchronous per-message confirmation.
|
|
242
|
+
#
|
|
243
|
+
# Close the sender explicitly when finished, or it will be closed when
|
|
244
|
+
# the client is closed.
|
|
245
|
+
#
|
|
246
|
+
# @return [PubSub::EventStoreSender] streaming sender bound to this client
|
|
247
|
+
#
|
|
248
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
249
|
+
# @raise [ConnectionError] if unable to reach the broker
|
|
250
|
+
#
|
|
251
|
+
# @see PubSub::EventStoreSender
|
|
252
|
+
def create_events_store_sender
|
|
253
|
+
ensure_connected!
|
|
254
|
+
sender = PubSub::EventStoreSender.new(transport: @transport, client_id: @config.client_id)
|
|
255
|
+
senders_mutex.synchronize { senders_list << sender }
|
|
256
|
+
sender
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Subscribes to durable events store messages on a channel. Runs on a
|
|
260
|
+
# background thread; incoming events are delivered to the provided block.
|
|
261
|
+
#
|
|
262
|
+
# The subscription automatically reconnects with exponential backoff on
|
|
263
|
+
# transient failures. On reconnect, playback resumes from the last
|
|
264
|
+
# received sequence number to avoid duplicates.
|
|
265
|
+
#
|
|
266
|
+
# @param subscription [PubSub::EventsStoreSubscription] channel, group,
|
|
267
|
+
# and start position config
|
|
268
|
+
# @param cancellation_token [CancellationToken, nil] token for cooperative
|
|
269
|
+
# cancellation (auto-created if nil)
|
|
270
|
+
# @param on_error [Proc, nil] callback receiving {Error} on stream or
|
|
271
|
+
# callback failures
|
|
272
|
+
# @yield [event] called for each received event on a background thread
|
|
273
|
+
# @yieldparam event [PubSub::EventStoreReceived] the received event
|
|
274
|
+
#
|
|
275
|
+
# @return [Subscription] handle to check status, cancel, or join
|
|
276
|
+
#
|
|
277
|
+
# @raise [ArgumentError] if no block is given
|
|
278
|
+
# @raise [ValidationError] if the subscription channel or start position
|
|
279
|
+
# is invalid
|
|
280
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
281
|
+
#
|
|
282
|
+
# @note On reconnect the subscription automatically resumes from the last
|
|
283
|
+
# received sequence number, overriding the original start position.
|
|
284
|
+
#
|
|
285
|
+
# @example Subscribe from the beginning
|
|
286
|
+
# sub = KubeMQ::PubSub::EventsStoreSubscription.new(
|
|
287
|
+
# channel: "orders",
|
|
288
|
+
# start_position: KubeMQ::PubSub::EventStoreStartPosition::START_FROM_FIRST
|
|
289
|
+
# )
|
|
290
|
+
# token = KubeMQ::CancellationToken.new
|
|
291
|
+
# client.subscribe_to_events_store(sub, cancellation_token: token) do |event|
|
|
292
|
+
# puts "seq=#{event.sequence}: #{event.body}"
|
|
293
|
+
# end
|
|
294
|
+
#
|
|
295
|
+
# @see PubSub::EventsStoreSubscription
|
|
296
|
+
# @see PubSub::EventStoreReceived
|
|
297
|
+
# @see PubSub::EventStoreStartPosition
|
|
298
|
+
# @see CancellationToken
|
|
299
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength -- subscription with auto-reconnect + sequence tracking
|
|
300
|
+
def subscribe_to_events_store(subscription, cancellation_token: nil, on_error: nil, &block)
|
|
301
|
+
raise ArgumentError, 'Block required for subscribe_to_events_store' unless block
|
|
302
|
+
|
|
303
|
+
Validator.validate_channel!(subscription.channel, allow_wildcards: false)
|
|
304
|
+
Validator.validate_events_store_subscription!(
|
|
305
|
+
subscription.start_position, subscription.start_position_value
|
|
306
|
+
)
|
|
307
|
+
ensure_connected!
|
|
308
|
+
|
|
309
|
+
cancellation_token ||= CancellationToken.new
|
|
310
|
+
nil
|
|
311
|
+
|
|
312
|
+
thread = Thread.new do
|
|
313
|
+
@transport.register_subscription(Thread.current)
|
|
314
|
+
Thread.current[:cancellation_token] = cancellation_token
|
|
315
|
+
last_sequence = 0
|
|
316
|
+
reconnect_attempts = 0
|
|
317
|
+
begin
|
|
318
|
+
loop do
|
|
319
|
+
break if cancellation_token.cancelled?
|
|
320
|
+
|
|
321
|
+
begin
|
|
322
|
+
@transport.ensure_connected!
|
|
323
|
+
proto_sub = Transport::Converter.subscribe_to_proto(subscription, @config.client_id)
|
|
324
|
+
if last_sequence.positive?
|
|
325
|
+
proto_sub.EventsStoreTypeData = PubSub::EventStoreStartPosition::START_AT_SEQUENCE
|
|
326
|
+
proto_sub.EventsStoreTypeValue = last_sequence + 1
|
|
327
|
+
end
|
|
328
|
+
stream = @transport.kubemq_client.subscribe_to_events(proto_sub)
|
|
329
|
+
Thread.current[:grpc_call] = stream
|
|
330
|
+
reconnect_attempts = 0
|
|
331
|
+
stream.each do |event_receive|
|
|
332
|
+
break if cancellation_token.cancelled?
|
|
333
|
+
|
|
334
|
+
hash = Transport::Converter.proto_to_event_received(event_receive)
|
|
335
|
+
received = PubSub::EventStoreReceived.new(**hash)
|
|
336
|
+
last_sequence = received.sequence if received.sequence.positive?
|
|
337
|
+
begin
|
|
338
|
+
block.call(received)
|
|
339
|
+
rescue StandardError => e
|
|
340
|
+
begin
|
|
341
|
+
on_error&.call(Error.new("Callback error: #{e.message}", code: ErrorCode::CALLBACK_ERROR))
|
|
342
|
+
rescue StandardError => nested
|
|
343
|
+
Kernel.warn("[kubemq] on_error callback raised: #{nested.message}")
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
rescue CancellationError
|
|
348
|
+
break
|
|
349
|
+
rescue GRPC::BadStatus, StandardError => e
|
|
350
|
+
@transport.on_disconnect! if e.is_a?(GRPC::Unavailable) || e.is_a?(GRPC::DeadlineExceeded)
|
|
351
|
+
reconnect_attempts += 1
|
|
352
|
+
begin
|
|
353
|
+
if e.is_a?(GRPC::BadStatus)
|
|
354
|
+
on_error&.call(ErrorMapper.map_grpc_error(e, operation: 'subscribe_events_store'))
|
|
355
|
+
else
|
|
356
|
+
on_error&.call(Error.new(e.message, code: ErrorCode::STREAM_BROKEN))
|
|
357
|
+
end
|
|
358
|
+
rescue StandardError => cb_err
|
|
359
|
+
Kernel.warn("[kubemq] on_error callback raised: #{cb_err.message}")
|
|
360
|
+
end
|
|
361
|
+
delay = [@config.reconnect_policy.base_interval *
|
|
362
|
+
(@config.reconnect_policy.multiplier**(reconnect_attempts - 1)),
|
|
363
|
+
@config.reconnect_policy.max_delay].min
|
|
364
|
+
sleep(delay) unless cancellation_token.cancelled?
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
rescue StandardError => e
|
|
368
|
+
Thread.current[:kubemq_subscription]&.mark_error(e)
|
|
369
|
+
ensure
|
|
370
|
+
begin; Thread.current[:grpc_call]&.cancel; rescue StandardError; end
|
|
371
|
+
Thread.current[:kubemq_subscription]&.mark_closed
|
|
372
|
+
@transport.unregister_subscription(Thread.current)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
sub_wrapper = KubeMQ::Subscription.new(thread: thread, cancellation_token: cancellation_token)
|
|
377
|
+
thread[:kubemq_subscription] = sub_wrapper
|
|
378
|
+
sub_wrapper
|
|
379
|
+
end
|
|
380
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
381
|
+
|
|
382
|
+
# Closes the client, all open senders, and releases resources.
|
|
383
|
+
#
|
|
384
|
+
# Closes any {PubSub::EventSender} or {PubSub::EventStoreSender}
|
|
385
|
+
# instances created through this client before closing the transport.
|
|
386
|
+
# This method is idempotent.
|
|
387
|
+
#
|
|
388
|
+
# @return [void]
|
|
389
|
+
def close
|
|
390
|
+
senders_mutex.synchronize do
|
|
391
|
+
senders_list.each do |s|
|
|
392
|
+
s.close
|
|
393
|
+
rescue StandardError
|
|
394
|
+
nil
|
|
395
|
+
end
|
|
396
|
+
senders_list.clear
|
|
397
|
+
end
|
|
398
|
+
super
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Creates an events channel on the broker.
|
|
402
|
+
#
|
|
403
|
+
# @param channel_name [String] name for the new channel
|
|
404
|
+
# @return [Boolean] +true+ on success
|
|
405
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
406
|
+
# @raise [ChannelError] if the broker rejects the operation
|
|
407
|
+
# @see BaseClient#create_channel
|
|
408
|
+
def create_events_channel(channel_name:)
|
|
409
|
+
create_channel(channel_name: channel_name, channel_type: ChannelType::EVENTS)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Creates an events store channel on the broker.
|
|
413
|
+
#
|
|
414
|
+
# @param channel_name [String] name for the new channel
|
|
415
|
+
# @return [Boolean] +true+ on success
|
|
416
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
417
|
+
# @raise [ChannelError] if the broker rejects the operation
|
|
418
|
+
# @see BaseClient#create_channel
|
|
419
|
+
def create_events_store_channel(channel_name:)
|
|
420
|
+
create_channel(channel_name: channel_name, channel_type: ChannelType::EVENTS_STORE)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Deletes an events channel from the broker.
|
|
424
|
+
#
|
|
425
|
+
# @param channel_name [String] name of the channel to delete
|
|
426
|
+
# @return [Boolean] +true+ on success
|
|
427
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
428
|
+
# @raise [ChannelError] if the broker rejects the operation
|
|
429
|
+
# @see BaseClient#delete_channel
|
|
430
|
+
def delete_events_channel(channel_name:)
|
|
431
|
+
delete_channel(channel_name: channel_name, channel_type: ChannelType::EVENTS)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# Deletes an events store channel from the broker.
|
|
435
|
+
#
|
|
436
|
+
# @param channel_name [String] name of the channel to delete
|
|
437
|
+
# @return [Boolean] +true+ on success
|
|
438
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
439
|
+
# @raise [ChannelError] if the broker rejects the operation
|
|
440
|
+
# @see BaseClient#delete_channel
|
|
441
|
+
def delete_events_store_channel(channel_name:)
|
|
442
|
+
delete_channel(channel_name: channel_name, channel_type: ChannelType::EVENTS_STORE)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Lists events channels, with optional name filtering.
|
|
446
|
+
#
|
|
447
|
+
# @param search [String, nil] substring filter for channel names
|
|
448
|
+
# @return [Array<ChannelInfo>] matching channels with metadata
|
|
449
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
450
|
+
# @see BaseClient#list_channels
|
|
451
|
+
def list_events_channels(search: nil)
|
|
452
|
+
list_channels(channel_type: ChannelType::EVENTS, search: search)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Lists events store channels, with optional name filtering.
|
|
456
|
+
#
|
|
457
|
+
# @param search [String, nil] substring filter for channel names
|
|
458
|
+
# @return [Array<ChannelInfo>] matching channels with metadata
|
|
459
|
+
# @raise [ClientClosedError] if the client has been closed
|
|
460
|
+
# @see BaseClient#list_channels
|
|
461
|
+
def list_events_store_channels(search: nil)
|
|
462
|
+
list_channels(channel_type: ChannelType::EVENTS_STORE, search: search)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
private
|
|
466
|
+
|
|
467
|
+
def senders_list
|
|
468
|
+
@senders_list ||= []
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def senders_mutex
|
|
472
|
+
@senders_mutex ||= Mutex.new
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module KubeMQ
|
|
6
|
+
module PubSub
|
|
7
|
+
# Outbound event message for fire-and-forget pub/sub.
|
|
8
|
+
#
|
|
9
|
+
# Construct an +EventMessage+ and pass it to {PubSubClient#send_event}
|
|
10
|
+
# or {PubSub::EventSender#publish}. Events are not persisted — use
|
|
11
|
+
# {EventStoreMessage} for durable delivery.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# msg = KubeMQ::PubSub::EventMessage.new(
|
|
15
|
+
# channel: "notifications.email",
|
|
16
|
+
# metadata: "user-signup",
|
|
17
|
+
# body: '{"user_id": 42}',
|
|
18
|
+
# tags: { "priority" => "high" }
|
|
19
|
+
# )
|
|
20
|
+
# result = client.send_event(msg)
|
|
21
|
+
#
|
|
22
|
+
# @see PubSubClient#send_event
|
|
23
|
+
# @see EventSender#publish
|
|
24
|
+
# @see EventStoreMessage
|
|
25
|
+
class EventMessage
|
|
26
|
+
# @!attribute [rw] id
|
|
27
|
+
# @return [String] unique message identifier (auto-generated UUID if not provided)
|
|
28
|
+
# @!attribute [rw] channel
|
|
29
|
+
# @return [String] target channel name
|
|
30
|
+
# @!attribute [rw] metadata
|
|
31
|
+
# @return [String, nil] arbitrary metadata string
|
|
32
|
+
# @!attribute [rw] body
|
|
33
|
+
# @return [String, nil] message payload (binary-safe)
|
|
34
|
+
# @!attribute [rw] tags
|
|
35
|
+
# @return [Hash{String => String}] user-defined key-value tags
|
|
36
|
+
attr_accessor :id, :channel, :metadata, :body, :tags
|
|
37
|
+
|
|
38
|
+
# @param channel [String] target channel name (required)
|
|
39
|
+
# @param metadata [String, nil] arbitrary metadata
|
|
40
|
+
# @param body [String, nil] message payload
|
|
41
|
+
# @param tags [Hash{String => String}, nil] key-value tags (default: +{}+)
|
|
42
|
+
# @param id [String, nil] message ID (default: auto-generated UUID)
|
|
43
|
+
def initialize(channel:, metadata: nil, body: nil, tags: nil, id: nil)
|
|
44
|
+
@id = id || SecureRandom.uuid
|
|
45
|
+
@channel = channel
|
|
46
|
+
@metadata = metadata
|
|
47
|
+
@body = body
|
|
48
|
+
@tags = tags || {}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KubeMQ
|
|
4
|
+
module PubSub
|
|
5
|
+
# An event received from a pub/sub subscription.
|
|
6
|
+
#
|
|
7
|
+
# Delivered to the block passed to {PubSubClient#subscribe_to_events}.
|
|
8
|
+
# This is a read-only snapshot — modifying attributes has no effect
|
|
9
|
+
# on the broker.
|
|
10
|
+
#
|
|
11
|
+
# @see PubSubClient#subscribe_to_events
|
|
12
|
+
# @see EventsSubscription
|
|
13
|
+
class EventReceived
|
|
14
|
+
# @!attribute [r] id
|
|
15
|
+
# @return [String] unique event identifier
|
|
16
|
+
# @!attribute [r] channel
|
|
17
|
+
# @return [String] the channel the event was published to
|
|
18
|
+
# @!attribute [r] metadata
|
|
19
|
+
# @return [String] event metadata
|
|
20
|
+
# @!attribute [r] body
|
|
21
|
+
# @return [String] event payload (binary)
|
|
22
|
+
# @!attribute [r] timestamp
|
|
23
|
+
# @return [Integer] broker-assigned timestamp (Unix nanoseconds)
|
|
24
|
+
# @!attribute [r] sequence
|
|
25
|
+
# @return [Integer] broker-assigned sequence number
|
|
26
|
+
# @!attribute [r] tags
|
|
27
|
+
# @return [Hash{String => String}] user-defined key-value tags
|
|
28
|
+
attr_reader :id, :channel, :metadata, :body, :timestamp, :sequence, :tags
|
|
29
|
+
|
|
30
|
+
# @param id [String] event identifier
|
|
31
|
+
# @param channel [String] source channel name
|
|
32
|
+
# @param metadata [String] event metadata
|
|
33
|
+
# @param body [String] event payload
|
|
34
|
+
# @param timestamp [Integer] broker timestamp (Unix nanoseconds)
|
|
35
|
+
# @param sequence [Integer] broker sequence number
|
|
36
|
+
# @param tags [Hash{String => String}, nil] key-value tags
|
|
37
|
+
def initialize(id:, channel:, metadata:, body:, timestamp:, sequence:, tags:)
|
|
38
|
+
@id = id
|
|
39
|
+
@channel = channel
|
|
40
|
+
@metadata = metadata
|
|
41
|
+
@body = body
|
|
42
|
+
@timestamp = timestamp
|
|
43
|
+
@sequence = sequence
|
|
44
|
+
@tags = tags || {}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|