fila-client 0.1.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.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # fila-ruby
2
+
3
+ Ruby client SDK for the [Fila](https://github.com/faisca/fila) message broker.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ gem install fila-client
9
+ ```
10
+
11
+ Or add to your Gemfile:
12
+
13
+ ```ruby
14
+ gem "fila-client"
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ require "fila"
21
+
22
+ client = Fila::Client.new("localhost:5555")
23
+
24
+ # Enqueue a message.
25
+ msg_id = client.enqueue(
26
+ queue: "my-queue",
27
+ headers: { "tenant" => "acme" },
28
+ payload: "hello world"
29
+ )
30
+ puts "Enqueued: #{msg_id}"
31
+
32
+ # Consume messages (block form).
33
+ client.consume(queue: "my-queue") do |msg|
34
+ puts "Received: #{msg.id} (attempt #{msg.attempt_count})"
35
+
36
+ begin
37
+ # Process the message...
38
+ client.ack(queue: "my-queue", msg_id: msg.id)
39
+ rescue => e
40
+ client.nack(queue: "my-queue", msg_id: msg.id, error: e.message)
41
+ end
42
+ end
43
+
44
+ client.close
45
+ ```
46
+
47
+ ## API
48
+
49
+ ### `Fila::Client.new(addr)`
50
+
51
+ Connect to a Fila broker at the given address (e.g., `"localhost:5555"`).
52
+
53
+ ### `client.enqueue(queue:, headers:, payload:)`
54
+
55
+ Enqueue a message. Returns the broker-assigned message ID (UUIDv7).
56
+
57
+ ### `client.consume(queue:) { |msg| ... }`
58
+
59
+ Open a streaming consumer. Yields `Fila::ConsumeMessage` objects as they become available. If no block is given, returns an `Enumerator`. Nacked messages are redelivered on the same stream.
60
+
61
+ ### `client.ack(queue:, msg_id:)`
62
+
63
+ Acknowledge a successfully processed message. The message is permanently removed.
64
+
65
+ ### `client.nack(queue:, msg_id:, error:)`
66
+
67
+ Negatively acknowledge a failed message. The message is requeued or routed to the dead-letter queue based on the queue's configuration.
68
+
69
+ ### `client.close`
70
+
71
+ Close the underlying gRPC channel.
72
+
73
+ ## Error Handling
74
+
75
+ Per-operation error classes are raised for specific failure modes:
76
+
77
+ ```ruby
78
+ begin
79
+ client.enqueue(queue: "missing-queue", payload: "test")
80
+ rescue Fila::QueueNotFoundError => e
81
+ # handle queue not found
82
+ end
83
+
84
+ begin
85
+ client.ack(queue: "my-queue", msg_id: "missing-id")
86
+ rescue Fila::MessageNotFoundError => e
87
+ # handle message not found
88
+ end
89
+ ```
90
+
91
+ ## License
92
+
93
+ AGPLv3
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc'
4
+
5
+ # Add proto directory to load path so generated requires resolve correctly.
6
+ $LOAD_PATH.unshift(File.expand_path('proto', __dir__)) unless $LOAD_PATH.include?(File.expand_path('proto', __dir__))
7
+
8
+ require_relative 'proto/fila/v1/service_services_pb'
9
+ require_relative 'errors'
10
+ require_relative 'consume_message'
11
+
12
+ module Fila
13
+ # Client for the Fila message broker.
14
+ #
15
+ # Wraps the hot-path gRPC operations: enqueue, consume, ack, nack.
16
+ #
17
+ # @example
18
+ # client = Fila::Client.new("localhost:5555")
19
+ # msg_id = client.enqueue(queue: "my-queue", headers: { "tenant" => "acme" }, payload: "hello")
20
+ # client.consume(queue: "my-queue") do |msg|
21
+ # client.ack(queue: "my-queue", msg_id: msg.id)
22
+ # break
23
+ # end
24
+ # client.close
25
+ class Client
26
+ # Connect to a Fila broker at the given address.
27
+ #
28
+ # @param addr [String] broker address in "host:port" format (e.g., "localhost:5555")
29
+ def initialize(addr)
30
+ @stub = ::Fila::V1::FilaService::Stub.new(addr, :this_channel_is_insecure)
31
+ end
32
+
33
+ # Close the underlying gRPC channel.
34
+ def close
35
+ # grpc-ruby doesn't expose a direct channel close on stubs;
36
+ # the channel is garbage-collected. This is a no-op for API symmetry.
37
+ end
38
+
39
+ # Enqueue a message to the specified queue.
40
+ #
41
+ # @param queue [String] target queue name
42
+ # @param headers [Hash<String, String>, nil] optional message headers
43
+ # @param payload [String] message payload bytes
44
+ # @return [String] broker-assigned message ID (UUIDv7)
45
+ # @raise [QueueNotFoundError] if the queue does not exist
46
+ # @raise [RPCError] for unexpected gRPC failures
47
+ def enqueue(queue:, payload:, headers: nil)
48
+ req = ::Fila::V1::EnqueueRequest.new(
49
+ queue: queue,
50
+ headers: headers || {},
51
+ payload: payload
52
+ )
53
+ resp = @stub.enqueue(req)
54
+ resp.message_id
55
+ rescue GRPC::NotFound => e
56
+ raise QueueNotFoundError, "enqueue: #{e.details}"
57
+ rescue GRPC::BadStatus => e
58
+ raise RPCError.new(e.code, e.details)
59
+ end
60
+
61
+ # Open a streaming consumer on the specified queue.
62
+ #
63
+ # Yields messages as they become available. Nil message frames (keepalive
64
+ # signals) are skipped automatically. Nacked messages are redelivered on
65
+ # the same stream.
66
+ #
67
+ # If no block is given, returns an Enumerator.
68
+ #
69
+ # @param queue [String] queue to consume from
70
+ # @yield [ConsumeMessage] each message received from the broker
71
+ # @return [Enumerator<ConsumeMessage>] if no block given
72
+ # @raise [QueueNotFoundError] if the queue does not exist
73
+ # @raise [RPCError] for unexpected gRPC failures
74
+ def consume(queue:, &block)
75
+ return enum_for(:consume, queue: queue) unless block
76
+
77
+ req = ::Fila::V1::ConsumeRequest.new(queue: queue)
78
+ stream = @stub.consume(req)
79
+ stream.each do |resp|
80
+ msg = resp.message
81
+ next if msg.nil? || msg.id.empty?
82
+
83
+ block.call(build_consume_message(msg))
84
+ end
85
+ rescue GRPC::Cancelled
86
+ # Stream cancelled — normal when consumer breaks out of the loop.
87
+ rescue GRPC::NotFound => e
88
+ raise QueueNotFoundError, "consume: #{e.details}"
89
+ rescue GRPC::BadStatus => e
90
+ raise RPCError.new(e.code, e.details)
91
+ end
92
+
93
+ # Acknowledge a successfully processed message.
94
+ #
95
+ # @param queue [String] queue the message belongs to
96
+ # @param msg_id [String] ID of the message to acknowledge
97
+ # @raise [MessageNotFoundError] if the message does not exist
98
+ # @raise [RPCError] for unexpected gRPC failures
99
+ def ack(queue:, msg_id:)
100
+ req = ::Fila::V1::AckRequest.new(queue: queue, message_id: msg_id)
101
+ @stub.ack(req)
102
+ nil
103
+ rescue GRPC::NotFound => e
104
+ raise MessageNotFoundError, "ack: #{e.details}"
105
+ rescue GRPC::BadStatus => e
106
+ raise RPCError.new(e.code, e.details)
107
+ end
108
+
109
+ # Negatively acknowledge a message that failed processing.
110
+ #
111
+ # @param queue [String] queue the message belongs to
112
+ # @param msg_id [String] ID of the message to nack
113
+ # @param error [String] description of the failure
114
+ # @raise [MessageNotFoundError] if the message does not exist
115
+ # @raise [RPCError] for unexpected gRPC failures
116
+ def nack(queue:, msg_id:, error:)
117
+ req = ::Fila::V1::NackRequest.new(queue: queue, message_id: msg_id, error: error)
118
+ @stub.nack(req)
119
+ nil
120
+ rescue GRPC::NotFound => e
121
+ raise MessageNotFoundError, "nack: #{e.details}"
122
+ rescue GRPC::BadStatus => e
123
+ raise RPCError.new(e.code, e.details)
124
+ end
125
+
126
+ private
127
+
128
+ def build_consume_message(msg)
129
+ metadata = msg.metadata
130
+ ConsumeMessage.new(
131
+ id: msg.id,
132
+ headers: msg.headers.to_h,
133
+ payload: msg.payload,
134
+ fairness_key: metadata&.fairness_key.to_s,
135
+ attempt_count: metadata&.attempt_count.to_i,
136
+ queue: metadata&.queue_id.to_s
137
+ )
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fila
4
+ # A message received from the broker via a consume stream.
5
+ ConsumeMessage = Struct.new(
6
+ :id, # Broker-assigned message ID (UUIDv7).
7
+ :headers, # Message headers (Hash<String, String>).
8
+ :payload, # Message payload bytes (String).
9
+ :fairness_key, # Fairness key for scheduling.
10
+ :attempt_count, # Number of previous delivery attempts.
11
+ :queue, # Queue the message belongs to.
12
+ keyword_init: true
13
+ )
14
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fila
4
+ # Base error for all Fila SDK errors.
5
+ class Error < StandardError; end
6
+
7
+ # Raised when the specified queue does not exist.
8
+ class QueueNotFoundError < Error; end
9
+
10
+ # Raised when the specified message does not exist.
11
+ class MessageNotFoundError < Error; end
12
+
13
+ # Raised for unexpected gRPC failures, preserving status code and message.
14
+ class RPCError < Error
15
+ # @return [Integer] gRPC status code
16
+ attr_reader :code
17
+
18
+ # @param code [Integer] gRPC status code
19
+ # @param message [String] error message
20
+ def initialize(code, message)
21
+ @code = code
22
+ super("rpc error (code = #{code}): #{message}")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: fila/v1/admin.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\n\x13\x66ila/v1/admin.proto\x12\x07\x66ila.v1\"H\n\x12\x43reateQueueRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x06\x63onfig\x18\x02 \x01(\x0b\x32\x14.fila.v1.QueueConfig\"b\n\x0bQueueConfig\x12\x19\n\x11on_enqueue_script\x18\x01 \x01(\t\x12\x19\n\x11on_failure_script\x18\x02 \x01(\t\x12\x1d\n\x15visibility_timeout_ms\x18\x03 \x01(\x04\"\'\n\x13\x43reateQueueResponse\x12\x10\n\x08queue_id\x18\x01 \x01(\t\"#\n\x12\x44\x65leteQueueRequest\x12\r\n\x05queue\x18\x01 \x01(\t\"\x15\n\x13\x44\x65leteQueueResponse\".\n\x10SetConfigRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\x13\n\x11SetConfigResponse\"\x1f\n\x10GetConfigRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\"\"\n\x11GetConfigResponse\x12\r\n\x05value\x18\x01 \x01(\t\")\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"#\n\x11ListConfigRequest\x12\x0e\n\x06prefix\x18\x01 \x01(\t\"P\n\x12ListConfigResponse\x12%\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x14.fila.v1.ConfigEntry\x12\x13\n\x0btotal_count\x18\x02 \x01(\r\" \n\x0fGetStatsRequest\x12\r\n\x05queue\x18\x01 \x01(\t\"b\n\x13PerFairnessKeyStats\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x15\n\rpending_count\x18\x02 \x01(\x04\x12\x17\n\x0f\x63urrent_deficit\x18\x03 \x01(\x03\x12\x0e\n\x06weight\x18\x04 \x01(\r\"Z\n\x13PerThrottleKeyStats\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0e\n\x06tokens\x18\x02 \x01(\x01\x12\x17\n\x0frate_per_second\x18\x03 \x01(\x01\x12\r\n\x05\x62urst\x18\x04 \x01(\x01\"\xec\x01\n\x10GetStatsResponse\x12\r\n\x05\x64\x65pth\x18\x01 \x01(\x04\x12\x11\n\tin_flight\x18\x02 \x01(\x04\x12\x1c\n\x14\x61\x63tive_fairness_keys\x18\x03 \x01(\x04\x12\x18\n\x10\x61\x63tive_consumers\x18\x04 \x01(\r\x12\x0f\n\x07quantum\x18\x05 \x01(\r\x12\x33\n\rper_key_stats\x18\x06 \x03(\x0b\x32\x1c.fila.v1.PerFairnessKeyStats\x12\x38\n\x12per_throttle_stats\x18\x07 \x03(\x0b\x32\x1c.fila.v1.PerThrottleKeyStats\"2\n\x0eRedriveRequest\x12\x11\n\tdlq_queue\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x02 \x01(\x04\"#\n\x0fRedriveResponse\x12\x10\n\x08redriven\x18\x01 \x01(\x04\"\x13\n\x11ListQueuesRequest\"U\n\tQueueInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05\x64\x65pth\x18\x02 \x01(\x04\x12\x11\n\tin_flight\x18\x03 \x01(\x04\x12\x18\n\x10\x61\x63tive_consumers\x18\x04 \x01(\r\"8\n\x12ListQueuesResponse\x12\"\n\x06queues\x18\x01 \x03(\x0b\x32\x12.fila.v1.QueueInfo2\xb4\x04\n\tFilaAdmin\x12H\n\x0b\x43reateQueue\x12\x1b.fila.v1.CreateQueueRequest\x1a\x1c.fila.v1.CreateQueueResponse\x12H\n\x0b\x44\x65leteQueue\x12\x1b.fila.v1.DeleteQueueRequest\x1a\x1c.fila.v1.DeleteQueueResponse\x12\x42\n\tSetConfig\x12\x19.fila.v1.SetConfigRequest\x1a\x1a.fila.v1.SetConfigResponse\x12\x42\n\tGetConfig\x12\x19.fila.v1.GetConfigRequest\x1a\x1a.fila.v1.GetConfigResponse\x12\x45\n\nListConfig\x12\x1a.fila.v1.ListConfigRequest\x1a\x1b.fila.v1.ListConfigResponse\x12?\n\x08GetStats\x12\x18.fila.v1.GetStatsRequest\x1a\x19.fila.v1.GetStatsResponse\x12<\n\x07Redrive\x12\x17.fila.v1.RedriveRequest\x1a\x18.fila.v1.RedriveResponse\x12\x45\n\nListQueues\x12\x1a.fila.v1.ListQueuesRequest\x1a\x1b.fila.v1.ListQueuesResponseb\x06proto3"
9
+
10
+ pool = ::Google::Protobuf::DescriptorPool.generated_pool
11
+ pool.add_serialized_file(descriptor_data)
12
+
13
+ module Fila
14
+ module V1
15
+ CreateQueueRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.CreateQueueRequest").msgclass
16
+ QueueConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.QueueConfig").msgclass
17
+ CreateQueueResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.CreateQueueResponse").msgclass
18
+ DeleteQueueRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.DeleteQueueRequest").msgclass
19
+ DeleteQueueResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.DeleteQueueResponse").msgclass
20
+ SetConfigRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.SetConfigRequest").msgclass
21
+ SetConfigResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.SetConfigResponse").msgclass
22
+ GetConfigRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.GetConfigRequest").msgclass
23
+ GetConfigResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.GetConfigResponse").msgclass
24
+ ConfigEntry = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ConfigEntry").msgclass
25
+ ListConfigRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ListConfigRequest").msgclass
26
+ ListConfigResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ListConfigResponse").msgclass
27
+ GetStatsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.GetStatsRequest").msgclass
28
+ PerFairnessKeyStats = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.PerFairnessKeyStats").msgclass
29
+ PerThrottleKeyStats = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.PerThrottleKeyStats").msgclass
30
+ GetStatsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.GetStatsResponse").msgclass
31
+ RedriveRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.RedriveRequest").msgclass
32
+ RedriveResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.RedriveResponse").msgclass
33
+ ListQueuesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ListQueuesRequest").msgclass
34
+ QueueInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.QueueInfo").msgclass
35
+ ListQueuesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ListQueuesResponse").msgclass
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: fila/v1/admin.proto for package 'fila.v1'
3
+
4
+ require 'grpc'
5
+ require 'fila/v1/admin_pb'
6
+
7
+ module Fila
8
+ module V1
9
+ module FilaAdmin
10
+ # Admin RPCs for operators and the CLI.
11
+ class Service
12
+
13
+ include ::GRPC::GenericService
14
+
15
+ self.marshal_class_method = :encode
16
+ self.unmarshal_class_method = :decode
17
+ self.service_name = 'fila.v1.FilaAdmin'
18
+
19
+ rpc :CreateQueue, ::Fila::V1::CreateQueueRequest, ::Fila::V1::CreateQueueResponse
20
+ rpc :DeleteQueue, ::Fila::V1::DeleteQueueRequest, ::Fila::V1::DeleteQueueResponse
21
+ rpc :SetConfig, ::Fila::V1::SetConfigRequest, ::Fila::V1::SetConfigResponse
22
+ rpc :GetConfig, ::Fila::V1::GetConfigRequest, ::Fila::V1::GetConfigResponse
23
+ rpc :ListConfig, ::Fila::V1::ListConfigRequest, ::Fila::V1::ListConfigResponse
24
+ rpc :GetStats, ::Fila::V1::GetStatsRequest, ::Fila::V1::GetStatsResponse
25
+ rpc :Redrive, ::Fila::V1::RedriveRequest, ::Fila::V1::RedriveResponse
26
+ rpc :ListQueues, ::Fila::V1::ListQueuesRequest, ::Fila::V1::ListQueuesResponse
27
+ end
28
+
29
+ Stub = Service.rpc_stub_class
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: fila/v1/messages.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'google/protobuf/timestamp_pb'
8
+
9
+
10
+ descriptor_data = "\n\x16\x66ila/v1/messages.proto\x12\x07\x66ila.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xe2\x01\n\x07Message\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\x07headers\x18\x02 \x03(\x0b\x32\x1d.fila.v1.Message.HeadersEntry\x12\x0f\n\x07payload\x18\x03 \x01(\x0c\x12*\n\x08metadata\x18\x04 \x01(\x0b\x32\x18.fila.v1.MessageMetadata\x12.\n\ntimestamps\x18\x05 \x01(\x0b\x32\x1a.fila.v1.MessageTimestamps\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"w\n\x0fMessageMetadata\x12\x14\n\x0c\x66\x61irness_key\x18\x01 \x01(\t\x12\x0e\n\x06weight\x18\x02 \x01(\r\x12\x15\n\rthrottle_keys\x18\x03 \x03(\t\x12\x15\n\rattempt_count\x18\x04 \x01(\r\x12\x10\n\x08queue_id\x18\x05 \x01(\t\"s\n\x11MessageTimestamps\x12/\n\x0b\x65nqueued_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12-\n\tleased_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestampb\x06proto3"
11
+
12
+ pool = ::Google::Protobuf::DescriptorPool.generated_pool
13
+ pool.add_serialized_file(descriptor_data)
14
+
15
+ module Fila
16
+ module V1
17
+ Message = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.Message").msgclass
18
+ MessageMetadata = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.MessageMetadata").msgclass
19
+ MessageTimestamps = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.MessageTimestamps").msgclass
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: fila/v1/service.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'fila/v1/messages_pb'
8
+
9
+
10
+ descriptor_data = "\n\x15\x66ila/v1/service.proto\x12\x07\x66ila.v1\x1a\x16\x66ila/v1/messages.proto\"\x97\x01\n\x0e\x45nqueueRequest\x12\r\n\x05queue\x18\x01 \x01(\t\x12\x35\n\x07headers\x18\x02 \x03(\x0b\x32$.fila.v1.EnqueueRequest.HeadersEntry\x12\x0f\n\x07payload\x18\x03 \x01(\x0c\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"%\n\x0f\x45nqueueResponse\x12\x12\n\nmessage_id\x18\x01 \x01(\t\"\x1f\n\x0e\x43onsumeRequest\x12\r\n\x05queue\x18\x01 \x01(\t\"4\n\x0f\x43onsumeResponse\x12!\n\x07message\x18\x01 \x01(\x0b\x32\x10.fila.v1.Message\"/\n\nAckRequest\x12\r\n\x05queue\x18\x01 \x01(\t\x12\x12\n\nmessage_id\x18\x02 \x01(\t\"\r\n\x0b\x41\x63kResponse\"?\n\x0bNackRequest\x12\r\n\x05queue\x18\x01 \x01(\t\x12\x12\n\nmessage_id\x18\x02 \x01(\t\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"\x0e\n\x0cNackResponse2\xf2\x01\n\x0b\x46ilaService\x12<\n\x07\x45nqueue\x12\x17.fila.v1.EnqueueRequest\x1a\x18.fila.v1.EnqueueResponse\x12>\n\x07\x43onsume\x12\x17.fila.v1.ConsumeRequest\x1a\x18.fila.v1.ConsumeResponse0\x01\x12\x30\n\x03\x41\x63k\x12\x13.fila.v1.AckRequest\x1a\x14.fila.v1.AckResponse\x12\x33\n\x04Nack\x12\x14.fila.v1.NackRequest\x1a\x15.fila.v1.NackResponseb\x06proto3"
11
+
12
+ pool = ::Google::Protobuf::DescriptorPool.generated_pool
13
+ pool.add_serialized_file(descriptor_data)
14
+
15
+ module Fila
16
+ module V1
17
+ EnqueueRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.EnqueueRequest").msgclass
18
+ EnqueueResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.EnqueueResponse").msgclass
19
+ ConsumeRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ConsumeRequest").msgclass
20
+ ConsumeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.ConsumeResponse").msgclass
21
+ AckRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.AckRequest").msgclass
22
+ AckResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.AckResponse").msgclass
23
+ NackRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.NackRequest").msgclass
24
+ NackResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("fila.v1.NackResponse").msgclass
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: fila/v1/service.proto for package 'fila.v1'
3
+
4
+ require 'grpc'
5
+ require 'fila/v1/service_pb'
6
+
7
+ module Fila
8
+ module V1
9
+ module FilaService
10
+ # Hot-path RPCs for producers and consumers.
11
+ class Service
12
+
13
+ include ::GRPC::GenericService
14
+
15
+ self.marshal_class_method = :encode
16
+ self.unmarshal_class_method = :decode
17
+ self.service_name = 'fila.v1.FilaService'
18
+
19
+ rpc :Enqueue, ::Fila::V1::EnqueueRequest, ::Fila::V1::EnqueueResponse
20
+ rpc :Consume, ::Fila::V1::ConsumeRequest, stream(::Fila::V1::ConsumeResponse)
21
+ rpc :Ack, ::Fila::V1::AckRequest, ::Fila::V1::AckResponse
22
+ rpc :Nack, ::Fila::V1::NackRequest, ::Fila::V1::NackResponse
23
+ end
24
+
25
+ Stub = Service.rpc_stub_class
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fila
4
+ VERSION = '0.1.0'
5
+ end
data/lib/fila.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fila/version'
4
+ require_relative 'fila/errors'
5
+ require_relative 'fila/consume_message'
6
+ require_relative 'fila/client'
@@ -0,0 +1,114 @@
1
+ syntax = "proto3";
2
+ package fila.v1;
3
+
4
+ // Admin RPCs for operators and the CLI.
5
+ service FilaAdmin {
6
+ rpc CreateQueue(CreateQueueRequest) returns (CreateQueueResponse);
7
+ rpc DeleteQueue(DeleteQueueRequest) returns (DeleteQueueResponse);
8
+ rpc SetConfig(SetConfigRequest) returns (SetConfigResponse);
9
+ rpc GetConfig(GetConfigRequest) returns (GetConfigResponse);
10
+ rpc ListConfig(ListConfigRequest) returns (ListConfigResponse);
11
+ rpc GetStats(GetStatsRequest) returns (GetStatsResponse);
12
+ rpc Redrive(RedriveRequest) returns (RedriveResponse);
13
+ rpc ListQueues(ListQueuesRequest) returns (ListQueuesResponse);
14
+ }
15
+
16
+ message CreateQueueRequest {
17
+ string name = 1;
18
+ QueueConfig config = 2;
19
+ }
20
+
21
+ message QueueConfig {
22
+ string on_enqueue_script = 1;
23
+ string on_failure_script = 2;
24
+ uint64 visibility_timeout_ms = 3;
25
+ }
26
+
27
+ message CreateQueueResponse {
28
+ string queue_id = 1;
29
+ }
30
+
31
+ message DeleteQueueRequest {
32
+ string queue = 1;
33
+ }
34
+
35
+ message DeleteQueueResponse {}
36
+
37
+ message SetConfigRequest {
38
+ string key = 1;
39
+ string value = 2;
40
+ }
41
+
42
+ message SetConfigResponse {}
43
+
44
+ message GetConfigRequest {
45
+ string key = 1;
46
+ }
47
+
48
+ message GetConfigResponse {
49
+ string value = 1;
50
+ }
51
+
52
+ message ConfigEntry {
53
+ string key = 1;
54
+ string value = 2;
55
+ }
56
+
57
+ message ListConfigRequest {
58
+ string prefix = 1;
59
+ }
60
+
61
+ message ListConfigResponse {
62
+ repeated ConfigEntry entries = 1;
63
+ uint32 total_count = 2;
64
+ }
65
+
66
+ message GetStatsRequest {
67
+ string queue = 1;
68
+ }
69
+
70
+ message PerFairnessKeyStats {
71
+ string key = 1;
72
+ uint64 pending_count = 2;
73
+ int64 current_deficit = 3;
74
+ uint32 weight = 4;
75
+ }
76
+
77
+ message PerThrottleKeyStats {
78
+ string key = 1;
79
+ double tokens = 2;
80
+ double rate_per_second = 3;
81
+ double burst = 4;
82
+ }
83
+
84
+ message GetStatsResponse {
85
+ uint64 depth = 1;
86
+ uint64 in_flight = 2;
87
+ uint64 active_fairness_keys = 3;
88
+ uint32 active_consumers = 4;
89
+ uint32 quantum = 5;
90
+ repeated PerFairnessKeyStats per_key_stats = 6;
91
+ repeated PerThrottleKeyStats per_throttle_stats = 7;
92
+ }
93
+
94
+ message RedriveRequest {
95
+ string dlq_queue = 1;
96
+ uint64 count = 2;
97
+ }
98
+
99
+ message RedriveResponse {
100
+ uint64 redriven = 1;
101
+ }
102
+
103
+ message ListQueuesRequest {}
104
+
105
+ message QueueInfo {
106
+ string name = 1;
107
+ uint64 depth = 2;
108
+ uint64 in_flight = 3;
109
+ uint32 active_consumers = 4;
110
+ }
111
+
112
+ message ListQueuesResponse {
113
+ repeated QueueInfo queues = 1;
114
+ }
@@ -0,0 +1,28 @@
1
+ syntax = "proto3";
2
+ package fila.v1;
3
+
4
+ import "google/protobuf/timestamp.proto";
5
+
6
+ // Core message envelope persisted in the broker.
7
+ message Message {
8
+ string id = 1;
9
+ map<string, string> headers = 2;
10
+ bytes payload = 3;
11
+ MessageMetadata metadata = 4;
12
+ MessageTimestamps timestamps = 5;
13
+ }
14
+
15
+ // Broker-assigned scheduling metadata.
16
+ message MessageMetadata {
17
+ string fairness_key = 1;
18
+ uint32 weight = 2;
19
+ repeated string throttle_keys = 3;
20
+ uint32 attempt_count = 4;
21
+ string queue_id = 5;
22
+ }
23
+
24
+ // Lifecycle timestamps attached to every message.
25
+ message MessageTimestamps {
26
+ google.protobuf.Timestamp enqueued_at = 1;
27
+ google.protobuf.Timestamp leased_at = 2;
28
+ }
@@ -0,0 +1,45 @@
1
+ syntax = "proto3";
2
+ package fila.v1;
3
+
4
+ import "fila/v1/messages.proto";
5
+
6
+ // Hot-path RPCs for producers and consumers.
7
+ service FilaService {
8
+ rpc Enqueue(EnqueueRequest) returns (EnqueueResponse);
9
+ rpc Consume(ConsumeRequest) returns (stream ConsumeResponse);
10
+ rpc Ack(AckRequest) returns (AckResponse);
11
+ rpc Nack(NackRequest) returns (NackResponse);
12
+ }
13
+
14
+ message EnqueueRequest {
15
+ string queue = 1;
16
+ map<string, string> headers = 2;
17
+ bytes payload = 3;
18
+ }
19
+
20
+ message EnqueueResponse {
21
+ string message_id = 1;
22
+ }
23
+
24
+ message ConsumeRequest {
25
+ string queue = 1;
26
+ }
27
+
28
+ message ConsumeResponse {
29
+ Message message = 1;
30
+ }
31
+
32
+ message AckRequest {
33
+ string queue = 1;
34
+ string message_id = 2;
35
+ }
36
+
37
+ message AckResponse {}
38
+
39
+ message NackRequest {
40
+ string queue = 1;
41
+ string message_id = 2;
42
+ string error = 3;
43
+ }
44
+
45
+ message NackResponse {}