Class: PactBroker::Integrations::Repository
- Inherits:
-
Object
- Object
- PactBroker::Integrations::Repository
- Includes:
- Repositories::Scopes
- Defined in:
- lib/pact_broker/integrations/repository.rb
Instance Method Summary collapse
- #create_for_pact(consumer_id, provider_id) ⇒ Object
-
#create_for_pacts(objects_with_consumer_and_provider) ⇒ Object
Ensure an Integration exists for each consumer/provider pair.
- #delete(consumer_id, provider_id) ⇒ Object
- #find(filter_options = {}, pagination_options = {}, eager_load_associations = []) ⇒ Object
-
#set_contract_data_updated_at(consumer, provider) ⇒ Object
Sets the contract_data_updated_at for the integration(s) as specified by the consumer and provider.
-
#set_contract_data_updated_at_for_multiple_integrations(objects_with_consumer_and_provider) ⇒ Object
Sets the contract_data_updated_at for the integrations as specified by an array of objects which each have a consumer and provider.
Methods included from Repositories::Scopes
#scope_for, #unscoped, #with_no_scope
Instance Method Details
#create_for_pact(consumer_id, provider_id) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/pact_broker/integrations/repository.rb', line 19 def create_for_pact(consumer_id, provider_id) if Integration.where(consumer_id: consumer_id, provider_id: provider_id).empty? Integration.new( consumer_id: consumer_id, provider_id: provider_id, created_at: Sequel.datetime_class.now, contract_data_updated_at: Sequel.datetime_class.now ).insert_ignore end nil end |
#create_for_pacts(objects_with_consumer_and_provider) ⇒ Object
Ensure an Integration exists for each consumer/provider pair. Using SELECT … INSERT IGNORE rather than just INSERT IGNORE so that we do not need to lock the table at all when the integrations already exist, which will be the most common use case. New integrations get created incredibly rarely. The INSERT IGNORE is used rather than just INSERT to handle race conditions when requests come in parallel.
38 39 40 41 42 43 |
# File 'lib/pact_broker/integrations/repository.rb', line 38 def create_for_pacts(objects_with_consumer_and_provider) published_integrations = objects_with_consumer_and_provider.collect{ |i| { consumer_id: i.consumer.id, provider_id: i.provider.id } } existing_integrations = Sequel::Model.db[:integrations].select(:consumer_id, :provider_id).where(Sequel.|(*published_integrations) ).all new_integrations = (published_integrations - existing_integrations).collect{ |i| i.merge(created_at: Sequel.datetime_class.now, contract_data_updated_at: Sequel.datetime_class.now) } Integration.dataset.insert_ignore.multi_insert(new_integrations) end |
#delete(consumer_id, provider_id) ⇒ Object
45 46 47 |
# File 'lib/pact_broker/integrations/repository.rb', line 45 def delete(consumer_id, provider_id) Integration.where(consumer_id: consumer_id, provider_id: provider_id).delete end |
#find(filter_options = {}, pagination_options = {}, eager_load_associations = []) ⇒ Object
10 11 12 13 14 15 16 17 |
# File 'lib/pact_broker/integrations/repository.rb', line 10 def find( = {}, = {}, eager_load_associations = []) query = scope_for(PactBroker::Integrations::Integration).select_all_qualified query = query.filter_by_pacticipant([:query_string]) if [:query_string] query .eager(*eager_load_associations) .order(Sequel.desc(:contract_data_updated_at, nulls: :last)) .() end |
#set_contract_data_updated_at(consumer, provider) ⇒ Object
Sets the contract_data_updated_at for the integration(s) as specified by the consumer and provider
52 53 54 |
# File 'lib/pact_broker/integrations/repository.rb', line 52 def set_contract_data_updated_at(consumer, provider) set_contract_data_updated_at_for_multiple_integrations([OpenStruct.new(consumer: consumer, provider: provider)]) end |
#set_contract_data_updated_at_for_multiple_integrations(objects_with_consumer_and_provider) ⇒ Object
Sets the contract_data_updated_at for the integrations as specified by an array of objects which each have a consumer and provider.
The contract_data_updated_at attribute is only ever used for ordering the list of integrations on the index page of the *Pact Broker* UI, so that the most recently updated integrations (the ones you’re likely working on) are showed at the top of the first page. There is often contention around updating it however, which can cause deadlocks, and slow down API responses. Because it’s not a critical field (eg. it won’t change any can-i-deploy results), the easiest way to reduce this contention is to just not update it if the row is locked, because if it is locked, the value of contract_data_updated_at is already going to be a date from a few seconds ago, which is perfectly fine for the purposes for which we are using the value.
Notes on SKIP LOCKED: SKIP LOCKED is only supported by Postgres. When executing SELECT … FOR UPDATE SKIP LOCKED, the SELECT will run immediately, not waiting for any other transactions, and only return rows that are not already locked by another transaction. The FOR UPDATE is required to make it work this way - SKIP LOCKED on its own does not work.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/pact_broker/integrations/repository.rb', line 73 def set_contract_data_updated_at_for_multiple_integrations(objects_with_consumer_and_provider) consumer_and_provider_ids = objects_with_consumer_and_provider.collect{ | object | { consumer_id: object.consumer&.id, provider_id: object.provider.id }.compact }.uniq # MySQL doesn't support an UPDATE with a subquery. FFS. Really need to do a major version release and delete the support code. criteria = if Integration.dataset.supports_skip_locked? integration_ids_to_update = Integration .select(:id) .where(Sequel.|(*consumer_and_provider_ids)) .for_update .skip_locked { id: integration_ids_to_update } else Sequel.|(*consumer_and_provider_ids) end Integration .where(criteria) .update(contract_data_updated_at: Sequel.datetime_class.now) end |