vectra-client 0.3.0 → 0.3.2
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 +4 -4
- data/CHANGELOG.md +86 -37
- data/SECURITY.md +134 -4
- data/docs/_layouts/page.html +2 -0
- data/docs/guides/monitoring.md +860 -0
- data/docs/guides/runbooks/cache-issues.md +267 -0
- data/docs/guides/runbooks/high-error-rate.md +152 -0
- data/docs/guides/runbooks/high-latency.md +287 -0
- data/docs/guides/runbooks/pool-exhausted.md +216 -0
- data/docs/guides/security.md +348 -0
- data/lib/vectra/audit_log.rb +225 -0
- data/lib/vectra/circuit_breaker.rb +336 -0
- data/lib/vectra/client.rb +2 -0
- data/lib/vectra/credential_rotation.rb +199 -0
- data/lib/vectra/health_check.rb +254 -0
- data/lib/vectra/instrumentation/honeybadger.rb +128 -0
- data/lib/vectra/instrumentation/sentry.rb +117 -0
- data/lib/vectra/logging.rb +242 -0
- data/lib/vectra/rate_limiter.rb +304 -0
- data/lib/vectra/version.rb +1 -1
- data/lib/vectra.rb +6 -0
- metadata +15 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "logger"
|
|
5
|
+
|
|
6
|
+
module Vectra
|
|
7
|
+
# Structured JSON logger for Vectra operations
|
|
8
|
+
#
|
|
9
|
+
# Provides consistent, machine-readable logging for all Vectra operations.
|
|
10
|
+
# Output is JSON formatted for easy parsing by log aggregators.
|
|
11
|
+
#
|
|
12
|
+
# @example Basic usage
|
|
13
|
+
# Vectra.configure do |config|
|
|
14
|
+
# config.logger = Vectra::JsonLogger.new(STDOUT)
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example With file output
|
|
18
|
+
# Vectra.configure do |config|
|
|
19
|
+
# config.logger = Vectra::JsonLogger.new("log/vectra.log")
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example With custom metadata
|
|
23
|
+
# logger = Vectra::JsonLogger.new(STDOUT, app: "my-app", env: "production")
|
|
24
|
+
#
|
|
25
|
+
class JsonLogger
|
|
26
|
+
SEVERITY_LABELS = {
|
|
27
|
+
Logger::DEBUG => "debug",
|
|
28
|
+
Logger::INFO => "info",
|
|
29
|
+
Logger::WARN => "warn",
|
|
30
|
+
Logger::ERROR => "error",
|
|
31
|
+
Logger::FATAL => "fatal"
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
attr_reader :output, :default_metadata
|
|
35
|
+
|
|
36
|
+
# Initialize JSON logger
|
|
37
|
+
#
|
|
38
|
+
# @param output [IO, String] Output destination (IO object or file path)
|
|
39
|
+
# @param metadata [Hash] Default metadata to include in all log entries
|
|
40
|
+
def initialize(output = $stdout, **metadata)
|
|
41
|
+
@output = resolve_output(output)
|
|
42
|
+
@default_metadata = metadata
|
|
43
|
+
@mutex = Mutex.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Log debug message
|
|
47
|
+
#
|
|
48
|
+
# @param message [String] Log message
|
|
49
|
+
# @param data [Hash] Additional data
|
|
50
|
+
def debug(message, **data)
|
|
51
|
+
log(Logger::DEBUG, message, **data)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Log info message
|
|
55
|
+
#
|
|
56
|
+
# @param message [String] Log message
|
|
57
|
+
# @param data [Hash] Additional data
|
|
58
|
+
def info(message, **data)
|
|
59
|
+
log(Logger::INFO, message, **data)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Log warning message
|
|
63
|
+
#
|
|
64
|
+
# @param message [String] Log message
|
|
65
|
+
# @param data [Hash] Additional data
|
|
66
|
+
def warn(message, **data)
|
|
67
|
+
log(Logger::WARN, message, **data)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Log error message
|
|
71
|
+
#
|
|
72
|
+
# @param message [String] Log message
|
|
73
|
+
# @param data [Hash] Additional data
|
|
74
|
+
def error(message, **data)
|
|
75
|
+
log(Logger::ERROR, message, **data)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Log fatal message
|
|
79
|
+
#
|
|
80
|
+
# @param message [String] Log message
|
|
81
|
+
# @param data [Hash] Additional data
|
|
82
|
+
def fatal(message, **data)
|
|
83
|
+
log(Logger::FATAL, message, **data)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Log Vectra operation event
|
|
87
|
+
#
|
|
88
|
+
# @param event [Instrumentation::Event] Operation event
|
|
89
|
+
def log_operation(event)
|
|
90
|
+
data = {
|
|
91
|
+
provider: event.provider.to_s,
|
|
92
|
+
operation: event.operation.to_s,
|
|
93
|
+
index: event.index,
|
|
94
|
+
duration_ms: event.duration,
|
|
95
|
+
success: event.success?
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Add metadata
|
|
99
|
+
data[:vector_count] = event.metadata[:vector_count] if event.metadata[:vector_count]
|
|
100
|
+
data[:result_count] = event.metadata[:result_count] if event.metadata[:result_count]
|
|
101
|
+
|
|
102
|
+
# Add error info if present
|
|
103
|
+
if event.error
|
|
104
|
+
data[:error_class] = event.error.class.name
|
|
105
|
+
data[:error_message] = event.error.message
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
level = event.success? ? Logger::INFO : Logger::ERROR
|
|
109
|
+
log(level, "vectra.#{event.operation}", **data)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Close the logger
|
|
113
|
+
#
|
|
114
|
+
# @return [void]
|
|
115
|
+
def close
|
|
116
|
+
@output.close if @output.respond_to?(:close) && @output != $stdout && @output != $stderr
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def log(severity, message, **data)
|
|
122
|
+
entry = build_entry(severity, message, data)
|
|
123
|
+
|
|
124
|
+
@mutex.synchronize do
|
|
125
|
+
@output.puts(JSON.generate(entry))
|
|
126
|
+
@output.flush if @output.respond_to?(:flush)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def build_entry(severity, message, data)
|
|
131
|
+
{
|
|
132
|
+
timestamp: Time.now.utc.iso8601(3),
|
|
133
|
+
level: SEVERITY_LABELS[severity],
|
|
134
|
+
logger: "vectra",
|
|
135
|
+
message: message
|
|
136
|
+
}.merge(default_metadata).merge(data).compact
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def resolve_output(output)
|
|
140
|
+
case output
|
|
141
|
+
when IO, StringIO
|
|
142
|
+
output
|
|
143
|
+
when String
|
|
144
|
+
File.open(output, "a")
|
|
145
|
+
else
|
|
146
|
+
$stdout
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Instrumentation handler for JSON logging
|
|
152
|
+
#
|
|
153
|
+
# @example Enable JSON logging
|
|
154
|
+
# require 'vectra/logging'
|
|
155
|
+
#
|
|
156
|
+
# Vectra::Logging.setup!(
|
|
157
|
+
# output: "log/vectra.json.log",
|
|
158
|
+
# app: "my-service"
|
|
159
|
+
# )
|
|
160
|
+
#
|
|
161
|
+
module Logging
|
|
162
|
+
class << self
|
|
163
|
+
attr_reader :logger
|
|
164
|
+
|
|
165
|
+
# Setup JSON logging for Vectra
|
|
166
|
+
#
|
|
167
|
+
# @param output [IO, String] Log output
|
|
168
|
+
# @param metadata [Hash] Default metadata
|
|
169
|
+
# @return [JsonLogger]
|
|
170
|
+
def setup!(output: $stdout, **metadata)
|
|
171
|
+
@logger = JsonLogger.new(output, **metadata)
|
|
172
|
+
|
|
173
|
+
# Register as instrumentation handler
|
|
174
|
+
Vectra::Instrumentation.on_operation do |event|
|
|
175
|
+
@logger.log_operation(event)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Also set as Vectra's logger for retry/error logging
|
|
179
|
+
Vectra.configuration.logger = @logger
|
|
180
|
+
|
|
181
|
+
@logger
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Log a custom event
|
|
185
|
+
#
|
|
186
|
+
# @param level [Symbol] Log level (:debug, :info, :warn, :error, :fatal)
|
|
187
|
+
# @param message [String] Log message
|
|
188
|
+
# @param data [Hash] Additional data
|
|
189
|
+
def log(level, message, **data)
|
|
190
|
+
return unless @logger
|
|
191
|
+
|
|
192
|
+
@logger.public_send(level, message, **data)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Log formatter for standard Ruby Logger that outputs JSON
|
|
198
|
+
#
|
|
199
|
+
# @example With standard Logger
|
|
200
|
+
# logger = Logger.new(STDOUT)
|
|
201
|
+
# logger.formatter = Vectra::JsonFormatter.new(app: "my-app")
|
|
202
|
+
#
|
|
203
|
+
class JsonFormatter
|
|
204
|
+
attr_reader :default_metadata
|
|
205
|
+
|
|
206
|
+
def initialize(**metadata)
|
|
207
|
+
@default_metadata = metadata
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def call(severity, time, _progname, message)
|
|
211
|
+
entry = {
|
|
212
|
+
timestamp: time.utc.iso8601(3),
|
|
213
|
+
level: severity.downcase,
|
|
214
|
+
logger: "vectra",
|
|
215
|
+
message: format_message(message)
|
|
216
|
+
}.merge(default_metadata)
|
|
217
|
+
|
|
218
|
+
# Parse structured data if message is a hash
|
|
219
|
+
if message.is_a?(Hash)
|
|
220
|
+
entry.merge!(message)
|
|
221
|
+
entry[:message] = message[:message] || message[:msg] || "operation"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
"#{JSON.generate(entry.compact)}\n"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def format_message(message)
|
|
230
|
+
case message
|
|
231
|
+
when String
|
|
232
|
+
message
|
|
233
|
+
when Exception
|
|
234
|
+
"#{message.class}: #{message.message}"
|
|
235
|
+
when Hash
|
|
236
|
+
message[:message] || message.to_s
|
|
237
|
+
else
|
|
238
|
+
message.to_s
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vectra
|
|
4
|
+
# Proactive rate limiter using token bucket algorithm
|
|
5
|
+
#
|
|
6
|
+
# Throttles requests BEFORE sending to prevent rate limit errors from providers.
|
|
7
|
+
# Uses token bucket algorithm for smooth rate limiting with burst support.
|
|
8
|
+
#
|
|
9
|
+
# @example Basic usage
|
|
10
|
+
# limiter = Vectra::RateLimiter.new(
|
|
11
|
+
# requests_per_second: 10,
|
|
12
|
+
# burst_size: 20
|
|
13
|
+
# )
|
|
14
|
+
#
|
|
15
|
+
# limiter.acquire do
|
|
16
|
+
# client.query(...)
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example With client wrapper
|
|
20
|
+
# client = Vectra::RateLimitedClient.new(
|
|
21
|
+
# Vectra::Client.new(...),
|
|
22
|
+
# requests_per_second: 10
|
|
23
|
+
# )
|
|
24
|
+
# client.query(...) # Automatically rate limited
|
|
25
|
+
#
|
|
26
|
+
class RateLimiter
|
|
27
|
+
class RateLimitExceededError < Vectra::Error
|
|
28
|
+
attr_reader :wait_time
|
|
29
|
+
|
|
30
|
+
def initialize(wait_time:)
|
|
31
|
+
@wait_time = wait_time
|
|
32
|
+
super("Rate limit exceeded. Retry after #{wait_time.round(2)} seconds")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_reader :requests_per_second, :burst_size
|
|
37
|
+
|
|
38
|
+
# Initialize rate limiter
|
|
39
|
+
#
|
|
40
|
+
# @param requests_per_second [Float] Sustained request rate
|
|
41
|
+
# @param burst_size [Integer] Maximum burst capacity (default: 2x RPS)
|
|
42
|
+
def initialize(requests_per_second:, burst_size: nil)
|
|
43
|
+
@requests_per_second = requests_per_second.to_f
|
|
44
|
+
@burst_size = burst_size || (requests_per_second * 2).to_i
|
|
45
|
+
@tokens = @burst_size.to_f
|
|
46
|
+
@last_refill = Time.now
|
|
47
|
+
@mutex = Mutex.new
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Acquire a token and execute block
|
|
51
|
+
#
|
|
52
|
+
# @param wait [Boolean] Wait for token if not available (default: true)
|
|
53
|
+
# @param timeout [Float] Maximum wait time in seconds (default: 30)
|
|
54
|
+
# @yield Block to execute after acquiring token
|
|
55
|
+
# @return [Object] Result of block
|
|
56
|
+
# @raise [RateLimitExceededError] If wait is false and no token available
|
|
57
|
+
def acquire(wait: true, timeout: 30, &)
|
|
58
|
+
acquired = try_acquire(wait: wait, timeout: timeout)
|
|
59
|
+
|
|
60
|
+
unless acquired
|
|
61
|
+
wait_time = time_until_token
|
|
62
|
+
raise RateLimitExceededError.new(wait_time: wait_time)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
yield
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Try to acquire a token without blocking
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true if token acquired
|
|
71
|
+
def try_acquire(wait: false, timeout: 30)
|
|
72
|
+
deadline = Time.now + timeout
|
|
73
|
+
|
|
74
|
+
loop do
|
|
75
|
+
@mutex.synchronize do
|
|
76
|
+
refill_tokens
|
|
77
|
+
if @tokens >= 1
|
|
78
|
+
@tokens -= 1
|
|
79
|
+
return true
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return false unless wait
|
|
84
|
+
return false if Time.now >= deadline
|
|
85
|
+
|
|
86
|
+
# Wait a bit before retrying
|
|
87
|
+
sleep([time_until_token, 0.1].min)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get current token count
|
|
92
|
+
#
|
|
93
|
+
# @return [Float]
|
|
94
|
+
def available_tokens
|
|
95
|
+
@mutex.synchronize do
|
|
96
|
+
refill_tokens
|
|
97
|
+
@tokens
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Get time until next token is available
|
|
102
|
+
#
|
|
103
|
+
# @return [Float] Seconds until next token
|
|
104
|
+
def time_until_token
|
|
105
|
+
@mutex.synchronize do
|
|
106
|
+
refill_tokens
|
|
107
|
+
return 0 if @tokens >= 1
|
|
108
|
+
|
|
109
|
+
tokens_needed = 1 - @tokens
|
|
110
|
+
tokens_needed / @requests_per_second
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get rate limiter statistics
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash]
|
|
117
|
+
def stats
|
|
118
|
+
@mutex.synchronize do
|
|
119
|
+
refill_tokens
|
|
120
|
+
{
|
|
121
|
+
requests_per_second: @requests_per_second,
|
|
122
|
+
burst_size: @burst_size,
|
|
123
|
+
available_tokens: @tokens.round(2),
|
|
124
|
+
time_until_token: @tokens >= 1 ? 0 : ((1 - @tokens) / @requests_per_second).round(3)
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Reset the rate limiter to full capacity
|
|
130
|
+
#
|
|
131
|
+
# @return [void]
|
|
132
|
+
def reset!
|
|
133
|
+
@mutex.synchronize do
|
|
134
|
+
@tokens = @burst_size.to_f
|
|
135
|
+
@last_refill = Time.now
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def refill_tokens
|
|
142
|
+
now = Time.now
|
|
143
|
+
elapsed = now - @last_refill
|
|
144
|
+
@last_refill = now
|
|
145
|
+
|
|
146
|
+
# Add tokens based on elapsed time
|
|
147
|
+
new_tokens = elapsed * @requests_per_second
|
|
148
|
+
@tokens = [@tokens + new_tokens, @burst_size.to_f].min
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Client wrapper with automatic rate limiting
|
|
153
|
+
#
|
|
154
|
+
# @example
|
|
155
|
+
# client = Vectra::RateLimitedClient.new(
|
|
156
|
+
# Vectra::Client.new(provider: :pinecone, ...),
|
|
157
|
+
# requests_per_second: 10,
|
|
158
|
+
# burst_size: 20
|
|
159
|
+
# )
|
|
160
|
+
#
|
|
161
|
+
# client.query(...) # Automatically waits if rate limited
|
|
162
|
+
#
|
|
163
|
+
class RateLimitedClient
|
|
164
|
+
RATE_LIMITED_METHODS = [:upsert, :query, :fetch, :update, :delete].freeze
|
|
165
|
+
|
|
166
|
+
attr_reader :client, :limiter
|
|
167
|
+
|
|
168
|
+
# Initialize rate limited client
|
|
169
|
+
#
|
|
170
|
+
# @param client [Client] The underlying Vectra client
|
|
171
|
+
# @param requests_per_second [Float] Request rate limit
|
|
172
|
+
# @param burst_size [Integer] Burst capacity
|
|
173
|
+
# @param wait [Boolean] Wait for rate limit (default: true)
|
|
174
|
+
def initialize(client, requests_per_second:, burst_size: nil, wait: true)
|
|
175
|
+
@client = client
|
|
176
|
+
@limiter = RateLimiter.new(
|
|
177
|
+
requests_per_second: requests_per_second,
|
|
178
|
+
burst_size: burst_size
|
|
179
|
+
)
|
|
180
|
+
@wait = wait
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Rate-limited upsert
|
|
184
|
+
def upsert(...)
|
|
185
|
+
with_rate_limit { client.upsert(...) }
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Rate-limited query
|
|
189
|
+
def query(...)
|
|
190
|
+
with_rate_limit { client.query(...) }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Rate-limited fetch
|
|
194
|
+
def fetch(...)
|
|
195
|
+
with_rate_limit { client.fetch(...) }
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Rate-limited update
|
|
199
|
+
def update(...)
|
|
200
|
+
with_rate_limit { client.update(...) }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Rate-limited delete
|
|
204
|
+
def delete(...)
|
|
205
|
+
with_rate_limit { client.delete(...) }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Pass through other methods without rate limiting
|
|
209
|
+
def method_missing(method, *, **, &)
|
|
210
|
+
if client.respond_to?(method)
|
|
211
|
+
client.public_send(method, *, **, &)
|
|
212
|
+
else
|
|
213
|
+
super
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def respond_to_missing?(method, include_private = false)
|
|
218
|
+
client.respond_to?(method, include_private) || super
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Get rate limiter stats
|
|
222
|
+
#
|
|
223
|
+
# @return [Hash]
|
|
224
|
+
def rate_limit_stats
|
|
225
|
+
limiter.stats
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def with_rate_limit(&)
|
|
231
|
+
limiter.acquire(wait: @wait, &)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Per-provider rate limiter registry
|
|
236
|
+
#
|
|
237
|
+
# @example
|
|
238
|
+
# # Configure rate limits per provider
|
|
239
|
+
# Vectra::RateLimiterRegistry.configure(:pinecone, requests_per_second: 100)
|
|
240
|
+
# Vectra::RateLimiterRegistry.configure(:qdrant, requests_per_second: 50)
|
|
241
|
+
#
|
|
242
|
+
# # Get limiter for provider
|
|
243
|
+
# limiter = Vectra::RateLimiterRegistry[:pinecone]
|
|
244
|
+
# limiter.acquire { ... }
|
|
245
|
+
#
|
|
246
|
+
module RateLimiterRegistry
|
|
247
|
+
class << self
|
|
248
|
+
# Configure rate limiter for provider
|
|
249
|
+
#
|
|
250
|
+
# @param provider [Symbol] Provider name
|
|
251
|
+
# @param requests_per_second [Float] Request rate
|
|
252
|
+
# @param burst_size [Integer] Burst capacity
|
|
253
|
+
# @return [RateLimiter]
|
|
254
|
+
def configure(provider, requests_per_second:, burst_size: nil)
|
|
255
|
+
limiters[provider.to_sym] = RateLimiter.new(
|
|
256
|
+
requests_per_second: requests_per_second,
|
|
257
|
+
burst_size: burst_size
|
|
258
|
+
)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Get limiter for provider
|
|
262
|
+
#
|
|
263
|
+
# @param provider [Symbol] Provider name
|
|
264
|
+
# @return [RateLimiter, nil]
|
|
265
|
+
def [](provider)
|
|
266
|
+
limiters[provider.to_sym]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Get all configured limiters
|
|
270
|
+
#
|
|
271
|
+
# @return [Hash<Symbol, RateLimiter>]
|
|
272
|
+
def all
|
|
273
|
+
limiters.dup
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Get stats for all limiters
|
|
277
|
+
#
|
|
278
|
+
# @return [Hash<Symbol, Hash>]
|
|
279
|
+
def stats
|
|
280
|
+
limiters.transform_values(&:stats)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Reset all limiters
|
|
284
|
+
#
|
|
285
|
+
# @return [void]
|
|
286
|
+
def reset_all!
|
|
287
|
+
limiters.each_value(&:reset!)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Clear all limiters
|
|
291
|
+
#
|
|
292
|
+
# @return [void]
|
|
293
|
+
def clear!
|
|
294
|
+
@limiters = {}
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
private
|
|
298
|
+
|
|
299
|
+
def limiters
|
|
300
|
+
@limiters ||= {}
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
data/lib/vectra/version.rb
CHANGED
data/lib/vectra.rb
CHANGED
|
@@ -11,6 +11,12 @@ require_relative "vectra/batch"
|
|
|
11
11
|
require_relative "vectra/streaming"
|
|
12
12
|
require_relative "vectra/cache"
|
|
13
13
|
require_relative "vectra/pool"
|
|
14
|
+
require_relative "vectra/circuit_breaker"
|
|
15
|
+
require_relative "vectra/rate_limiter"
|
|
16
|
+
require_relative "vectra/logging"
|
|
17
|
+
require_relative "vectra/health_check"
|
|
18
|
+
require_relative "vectra/credential_rotation"
|
|
19
|
+
require_relative "vectra/audit_log"
|
|
14
20
|
require_relative "vectra/active_record"
|
|
15
21
|
require_relative "vectra/providers/base"
|
|
16
22
|
require_relative "vectra/providers/pinecone"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vectra-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mijo Kristo
|
|
@@ -258,7 +258,13 @@ files:
|
|
|
258
258
|
- docs/examples/index.md
|
|
259
259
|
- docs/guides/getting-started.md
|
|
260
260
|
- docs/guides/installation.md
|
|
261
|
+
- docs/guides/monitoring.md
|
|
261
262
|
- docs/guides/performance.md
|
|
263
|
+
- docs/guides/runbooks/cache-issues.md
|
|
264
|
+
- docs/guides/runbooks/high-error-rate.md
|
|
265
|
+
- docs/guides/runbooks/high-latency.md
|
|
266
|
+
- docs/guides/runbooks/pool-exhausted.md
|
|
267
|
+
- docs/guides/security.md
|
|
262
268
|
- docs/index.md
|
|
263
269
|
- docs/providers/index.md
|
|
264
270
|
- docs/providers/pgvector.md
|
|
@@ -272,14 +278,21 @@ files:
|
|
|
272
278
|
- lib/generators/vectra/templates/vectra.rb
|
|
273
279
|
- lib/vectra.rb
|
|
274
280
|
- lib/vectra/active_record.rb
|
|
281
|
+
- lib/vectra/audit_log.rb
|
|
275
282
|
- lib/vectra/batch.rb
|
|
276
283
|
- lib/vectra/cache.rb
|
|
284
|
+
- lib/vectra/circuit_breaker.rb
|
|
277
285
|
- lib/vectra/client.rb
|
|
278
286
|
- lib/vectra/configuration.rb
|
|
287
|
+
- lib/vectra/credential_rotation.rb
|
|
279
288
|
- lib/vectra/errors.rb
|
|
289
|
+
- lib/vectra/health_check.rb
|
|
280
290
|
- lib/vectra/instrumentation.rb
|
|
281
291
|
- lib/vectra/instrumentation/datadog.rb
|
|
292
|
+
- lib/vectra/instrumentation/honeybadger.rb
|
|
282
293
|
- lib/vectra/instrumentation/new_relic.rb
|
|
294
|
+
- lib/vectra/instrumentation/sentry.rb
|
|
295
|
+
- lib/vectra/logging.rb
|
|
283
296
|
- lib/vectra/pool.rb
|
|
284
297
|
- lib/vectra/providers/base.rb
|
|
285
298
|
- lib/vectra/providers/pgvector.rb
|
|
@@ -290,6 +303,7 @@ files:
|
|
|
290
303
|
- lib/vectra/providers/qdrant.rb
|
|
291
304
|
- lib/vectra/providers/weaviate.rb
|
|
292
305
|
- lib/vectra/query_result.rb
|
|
306
|
+
- lib/vectra/rate_limiter.rb
|
|
293
307
|
- lib/vectra/retry.rb
|
|
294
308
|
- lib/vectra/streaming.rb
|
|
295
309
|
- lib/vectra/vector.rb
|