Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing enable retries #78

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
20 changes: 5 additions & 15 deletions lib/novu/api/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,20 @@ def delete(path, options = {})
# It applies exponential backoff strategy (if enabled) for failed requests.
# It also performs an idempotent request to safely retry requests without having duplication operations.
#
def request(http_method, path, options)
def request(http_method, path, options, retry_number: 0)
raise StandardError, "Max retry attempts reached" if retry_number >= @max_retries

if http_method.to_s == 'post' || http_method.to_s == 'patch'
self.class.default_options[:headers].merge!({ "Idempotency-Key" => "#{@idempotency_key.to_s.strip}" })
end

response = self.class.send(http_method, path, options)

if ! [401, 403, 409, 500, 502, 503, 504].include?(response.code) && ! @enable_retry
if ! [500, 502, 503, 504].include?(response.code) || ! @enable_retry
response
elsif @enable_retry

if @retry_attempts < @max_retries
@retry_attempts += 1

@backoff.intervals.each do |interval|
sleep(interval)
request(http_method, path, options)
end
else
raise StandardError, "Max retry attempts reached"
end
else
response
sleep(@backoff.interval_at(retry_number))
request(http_method, path, options, retry_number: retry_number + 1)
end
end
end
Expand Down
97 changes: 97 additions & 0 deletions spec/client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# frozen_string_literal: true

require_relative "../lib/novu"

RSpec.describe Novu::Client do

let(:access_token) { "1234567890" }
let(:idempotency_key) { "489e2e70-50be-013c-faf9-38e85644422a" }

let(:body) {
{
firstName: "John",
lastName: "Doe"
}.to_json
}

let(:response_body) {
{
_id: "63f71b3ef067290fa669106d"
}.to_json
}

let(:max_retries) { 3 }
let(:success_response) { TestResponse.new(code: 201, body: response_body) }
let(:failure_response) { TestResponse.new(code: 500, body: response_body) }

context "with exponential retry enabled" do
let(:client) {
Novu::Client.new(
access_token: access_token,
idempotency_key: idempotency_key,
enable_retry: true,
retry_config: {
max_retries: max_retries,
initial_delay: 0,
max_delay: 0
}
)}

context "when all retries fail" do
before do
allow(Novu::Client).to receive(:post).exactly(max_retries).and_return(failure_response)
end
it "throws an error" do
expect { client.create_subscriber(body) }.to raise_error(StandardError)
end
end

context "as soon as a request succeeds" do
before do
allow(Novu::Client).to receive(:post).at_most(2).and_return(failure_response, success_response)
end

it "should return successfully" do
result = client.create_subscriber(body)
expect(result[:status]).to eq(201)
end
end

end

context "with exponential retry disabled" do
let(:client) { Novu::Client.new(access_token: access_token, idempotency_key: idempotency_key) }

it "failed requests are not retried" do
allow(Novu::Client).to receive(:post).exactly(1).and_return(failure_response)

result = client.create_subscriber(body)
expect(result[:status]).to eq(500)
end

it "performs idempotent request with no duplicate result" do
allow(client).to receive(:request).at_most(3).and_return(success_response)
result1 = client.create_subscriber(body)
result2 = client.create_subscriber(body)
result3 = client.create_subscriber(body)

expect(result1[:body]).to eq(response_body)
expect(result2[:body]).to eq(response_body)
expect(result3[:body]).to eq(response_body)
end
end

end

class TestResponse
attr_accessor :code, :body

def initialize(code: 201, body: "")
@code = code
@body = body
end

def [](attr)
attr == :status ? code : body
end
end
57 changes: 0 additions & 57 deletions test/unit/client_spec.rb

This file was deleted.