diff --git a/app/models/cells/outstanding_lease.rb b/app/models/cells/outstanding_lease.rb index f74763e6b828aee1a164b14748573b816477a59a..07d35c9f2226d2e303117418b7ee2375fa17d8d6 100644 --- a/app/models/cells/outstanding_lease.rb +++ b/app/models/cells/outstanding_lease.rb @@ -3,5 +3,41 @@ module Cells class OutstandingLease < ApplicationRecord self.primary_key = :uuid + + def self.claim_service + ::Gitlab::TopologyServiceClient::ClaimService.instance # rubocop:disable CodeReuse/ServiceClass -- this is a gRPC client + end + + def self.create_from_request!(create_records:, destroy_records:, deadline: nil) + req = Gitlab::Cells::TopologyService::Claims::V1::BeginUpdateRequest.new( + create_records: create_records, + destroy_records: destroy_records, + cell_id: claim_service.cell_id + ) + + res = claim_service.begin_update(req, deadline: deadline) + + create!(uuid: res.lease_uuid.value) + end + + def send_commit_update!(deadline: nil) + req = Gitlab::Cells::TopologyService::Claims::V1::CommitUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid), + cell_id: self.class.claim_service.cell_id + ) + + self.class.claim_service.commit_update(req, deadline: deadline) + end + + def send_rollback_update!(deadline: nil) + return unless uuid + + req = Gitlab::Cells::TopologyService::Claims::V1::RollbackUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid), + cell_id: self.class.claim_service.cell_id + ) + + self.class.claim_service.rollback_update(req, deadline: deadline) + end end end diff --git a/spec/models/cells/outstanding_lease_spec.rb b/spec/models/cells/outstanding_lease_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..175c37217dc15229efac14a442de2de3d3ab8487 --- /dev/null +++ b/spec/models/cells/outstanding_lease_spec.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Cells::OutstandingLease, feature_category: :cell do + let(:cell_id) { 1 } + let(:uuid_value) { SecureRandom.uuid } + let(:mock_service) { instance_double(::Gitlab::TopologyServiceClient::ClaimService) } + + before do + allow(described_class).to receive(:claim_service).and_return(mock_service) + allow(mock_service).to receive(:cell_id).and_return(cell_id) + end + + describe '.create_from_request!' do + let(:create_records) { [{}] } + let(:destroy_records) { [{}] } + + let(:mock_response) do + Gitlab::Cells::TopologyService::Claims::V1::BeginUpdateResponse.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid_value) + ) + end + + it 'calls begin_update and creates an OutstandingLease' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::BeginUpdateRequest.new( + create_records: create_records, + destroy_records: destroy_records, + cell_id: cell_id + ) + + expect(mock_service) + .to receive(:begin_update).with(expected_request, deadline: nil).and_return(mock_response) + + lease = described_class.create_from_request!(create_records: create_records, destroy_records: destroy_records) + + expect(lease).to be_persisted + expect(lease.uuid).to eq(uuid_value) + end + + context 'with deadline' do + let(:deadline) { GRPC::Core::TimeConsts.from_relative_time(5.0) } + + it 'calls begin_update with deadline and creates an OutstandingLease' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::BeginUpdateRequest.new( + create_records: create_records, + destroy_records: destroy_records, + cell_id: cell_id + ) + + expect(mock_service) + .to receive(:begin_update).with(expected_request, deadline: deadline).and_return(mock_response) + + lease = described_class.create_from_request!( + create_records: create_records, + destroy_records: destroy_records, + deadline: deadline + ) + + expect(lease).to be_persisted + expect(lease.uuid).to eq(uuid_value) + end + end + end + + describe '#send_commit_update!' do + let(:lease) { described_class.create!(uuid: uuid_value) } + + it 'sends a commit_update request with the correct parameters' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::CommitUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid_value), + cell_id: cell_id + ) + + expect(mock_service).to receive(:commit_update).with(expected_request, deadline: nil) + + lease.send_commit_update! + end + + context 'with deadline' do + let(:deadline) { GRPC::Core::TimeConsts.from_relative_time(5.0) } + + it 'sends a commit_update request with the correct parameters' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::CommitUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid_value), + cell_id: cell_id + ) + + expect(mock_service).to receive(:commit_update).with(expected_request, deadline: deadline) + + lease.send_commit_update!(deadline: deadline) + end + end + end + + describe '#send_rollback_update!' do + context 'when uuid is present' do + let(:lease) { described_class.create!(uuid: uuid_value) } + + it 'sends a rollback_update request with the correct parameters' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::RollbackUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid_value), + cell_id: cell_id + ) + + expect(mock_service).to receive(:rollback_update).with(expected_request, deadline: nil) + + lease.send_rollback_update! + end + + context 'with deadline' do + let(:deadline) { GRPC::Core::TimeConsts.from_relative_time(5.0) } + + it 'sends a rollback_update request with the correct parameters' do + expected_request = Gitlab::Cells::TopologyService::Claims::V1::RollbackUpdateRequest.new( + lease_uuid: Gitlab::Cells::TopologyService::Types::V1::UUID.new(value: uuid_value), + cell_id: cell_id + ) + + expect(mock_service).to receive(:rollback_update).with(expected_request, deadline: deadline) + + lease.send_rollback_update!(deadline: deadline) + end + end + end + + context 'when uuid is nil' do + let(:lease) { described_class.new(uuid: nil) } + + it 'does not send a rollback_update request' do + expect(mock_service).not_to receive(:rollback_update) + + lease.send_rollback_update! + end + end + end + + describe '.claim_service' do + it 'returns the ClaimService instance' do + expect(described_class.claim_service).to eq(mock_service) + end + end +end