Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ Here are some examples with inline comments that walk you through how to use the

Tests are also a good place to know how the the library works internally: [spec](spec)

### Error handling

Failed requests raise a subclass of `Typesense::Error`. The exception's `message` is the API error string (`"Object Not Found"`, etc.), and the underlying Faraday response is available on `#data` for callers that need the HTTP status, headers, or raw body:

```ruby
begin
client.collections['unknown'].retrieve
rescue Typesense::Error::ObjectNotFound => e
e.message # => "Not Found"
e.data.status # => 404
e.data.body # => raw response body
end
```

The exception classes that map to specific status codes are: `RequestMalformed` (400), `RequestUnauthorized` (401), `ObjectNotFound` (404), `ObjectAlreadyExists` (409), `ObjectUnprocessable` (422), `ServerError` (5xx), `HTTPStatus0Error` (status 0), `TimeoutError`, and `HTTPError` (anything else).

### Keep-alive connections

By default, the client opens a fresh HTTP connection (and TLS handshake) for every request. For high-traffic applications this can dominate request latency. Setting `keep_alive_connections: true` enables persistent connections via the `:net_http_persistent` Faraday adapter:
Expand Down
3 changes: 2 additions & 1 deletion lib/typesense/analytics_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class AnalyticsRules

def initialize(api_call)
@api_call = api_call
@analytics_rules = {}
end

def create(rules)
Expand All @@ -17,7 +18,7 @@ def retrieve
end

def [](rule_name)
AnalyticsRule.new(rule_name, @api_call)
@analytics_rules[rule_name] ||= AnalyticsRule.new(rule_name, @api_call)
end
end
end
4 changes: 2 additions & 2 deletions lib/typesense/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(options = {})
@nearest_node = options[:nearest_node]
@connection_timeout_seconds = options[:connection_timeout_seconds] || options[:timeout_seconds] || 10
@healthcheck_interval_seconds = options[:healthcheck_interval_seconds] || 15
@num_retries = options[:num_retries] || (@nodes.length + (@nearest_node.nil? ? 0 : 1)) || 3
@num_retries = options[:num_retries] || [@nodes.length + (@nearest_node.nil? ? 0 : 1), 3].max
@retry_interval_seconds = options[:retry_interval_seconds] || 0.1
@api_key = options[:api_key]
@keep_alive_connections = options.fetch(:keep_alive_connections, false)
Expand Down Expand Up @@ -41,7 +41,7 @@ def validate!(options = {})
private

def node_missing_parameters?(node)
%i[protocol host port].any? { |attr| node.send(:[], attr).nil? }
%i[protocol host port].any? { |attr| node[attr].nil? }
end

def validate_keep_alive_options!(options)
Expand Down
3 changes: 2 additions & 1 deletion lib/typesense/curation_sets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CurationSets

def initialize(api_call)
@api_call = api_call
@curation_sets = {}
end

def upsert(curation_set_name, curation_set_data)
Expand All @@ -17,7 +18,7 @@ def retrieve
end

def [](curation_set_name)
CurationSet.new(curation_set_name, @api_call)
@curation_sets[curation_set_name] ||= CurationSet.new(curation_set_name, @api_call)
end
end
end
4 changes: 2 additions & 2 deletions spec/typesense/analytics_rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@
expect(result.instance_variable_get(:@rule_name)).to eq(rule_name)
end

it 'does not memoize the analytics rule instance' do
it 'memoizes the analytics rule instance' do
first_call = integration_client.analytics.rules[rule_name]
second_call = integration_client.analytics.rules[rule_name]
expect(first_call).not_to equal(second_call)
expect(first_call).to equal(second_call)
end
end
end
43 changes: 43 additions & 0 deletions spec/typesense/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,47 @@
end
end
end

describe '#num_retries default' do
let(:base_options) do
{
api_key: 'abcd',
nodes: [
{ host: 'node0', port: 8108, protocol: 'http' },
{ host: 'node1', port: 8108, protocol: 'http' },
{ host: 'node2', port: 8108, protocol: 'http' }
],
log_level: Logger::ERROR
}
end

it 'defaults to the number of nodes when no nearest_node is set' do
config = described_class.new(base_options)
expect(config.num_retries).to eq(3)
end

it 'defaults to nodes.length + 1 when a nearest_node is set' do
config = described_class.new(
base_options.merge(nearest_node: { host: 'nearestNode', port: 6108, protocol: 'http' })
)
expect(config.num_retries).to eq(4)
end

it 'falls back to a minimum of 3 for single-node setups' do
config = described_class.new(
base_options.merge(nodes: [{ host: 'node0', port: 8108, protocol: 'http' }])
)
expect(config.num_retries).to eq(3)
end

it 'honors an explicit num_retries option' do
config = described_class.new(base_options.merge(num_retries: 7))
expect(config.num_retries).to eq(7)
end

it 'honors an explicit num_retries of 0 (Integer is truthy in Ruby)' do
config = described_class.new(base_options.merge(num_retries: 0))
expect(config.num_retries).to eq(0)
end
end
end
Loading