Skip to content

Commit 1ce4549

Browse files
committed
Remove live_pollable_exists guard from create, not needed here.
1 parent c1f257c commit 1ce4549

4 files changed

Lines changed: 8 additions & 56 deletions

app/jobs/runtime/service_operations_binding_delete_stuck_in_progress_retry.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@ def max_attempts
1919
def retry_stuck(operation_model, instance_model, foreign_key, jobs_operation)
2020
# Find stuck binding 'delete' operations where the broker may still be working
2121
# but CC's polling job has permanently failed due to a transient error (e.g. brief db connection flip).
22-
#
23-
# Unlike create we do not mark the operation failed and do not mitigate orphans: for a delete we
24-
# re-enqueue the original polling job so the unbind is driven to completion. The original delayed_job's
22+
# We re-enqueue the original polling job so the unbind is driven to completion. The original delayed_job's
2523
# serialized handler is reused, preserving @start_time so the ReoccurringJob max-duration expiry
26-
# (which marks the operation failed via handle_timeout) still fires against the original polling window.
24+
# still fires against the original polling window.
2725
operation_table = operation_model.table_name
2826
instance_table = instance_model.table_name
2927

@@ -80,8 +78,7 @@ def resolve_stuck(operation_model, instance_model, op_id, resource_id, pollable_
8078
end
8179

8280
# Reuse the original delete polling job by deserializing the failed delayed_job's handler and unwrapping
83-
# the wrapper chain (LoggingContextJob → TimeoutJob → PollableJobWrapper → DeleteBindingJob).
84-
# This preserves the original @user_audit_info, @start_time and the binding @type.
81+
# the wrapper chain. This preserves the original @user_audit_info, @start_time and the binding @type.
8582
def deserialize_handler(pollable)
8683
delayed_job = Delayed::Job[guid: pollable.delayed_job_guid]
8784
return unless delayed_job

app/jobs/runtime/service_operations_create_in_progress_cleanup.rb

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ def cleanup_operations(operation_model, instance_model, foreign_key, jobs_operat
4040
# service instance that happen to share the same resource_guid
4141
# - delayed_jobs.failed_at IS NOT NULL: the delayed job permanently failed (exhausted max_attempts);
4242
# jobs still alive or locked have failed_at=NULL and must not be touched
43-
# - service_instances.guid NOT IN (live pollables for this operation): skip resources that still
44-
# have a POLLING/PROCESSING pollable driving this operation. A prior operation on the same
45-
# resource can leave a stale, permanently-failed pollable behind; without this guard that dead
46-
# row would match by resource_guid and cause the current healthy operation to be marked failed
4743
operation_table = operation_model.table_name
4844
instance_table = instance_model.table_name
4945

@@ -57,7 +53,6 @@ def cleanup_operations(operation_model, instance_model, foreign_key, jobs_operat
5753
where(Sequel[:jobs][:state] => [PollableJobModel::POLLING_STATE, PollableJobModel::FAILED_STATE]).
5854
where(Sequel[:jobs][:operation] => jobs_operation).
5955
exclude(Sequel[:delayed_jobs][:failed_at] => nil).
60-
exclude(live_pollable_exists(operation_model, instance_table, jobs_operation)).
6156
select(
6257
Sequel[:jobs][:guid].as(:pollable_guid),
6358
Sequel[operation_table][:id].as(:op_id),
@@ -109,25 +104,6 @@ def default_maximum_duration_seconds
109104
Config.config.get(:broker_client_max_async_poll_duration_minutes).minutes
110105
end
111106

112-
# NOT EXISTS guard: skip a resource if it still has a pollable job actively driving
113-
# THIS operation — state POLLING or PROCESSING AND backed by a delayed_job that has
114-
# NOT permanently failed (failed_at IS NULL, or no delayed_job row yet). A stale,
115-
# permanently-failed pollable left behind by a previous operation on the same
116-
# resource must NOT cause the current healthy operation to be marked failed. A
117-
# POLLING pollable whose delayed_job IS failed is itself stuck (the DB flip happened
118-
# before the failure hook could write FAILED) and must NOT count as live. Correlated
119-
# (resource_guid = instance.guid) so a NULL jobs.resource_guid elsewhere cannot
120-
# poison the result the way a NOT IN subquery would.
121-
def live_pollable_exists(operation_model, instance_table, jobs_operation)
122-
operation_model.db[:jobs].
123-
left_join(:delayed_jobs, guid: Sequel[:jobs][:delayed_job_guid]).
124-
where(Sequel[:jobs][:operation] => jobs_operation).
125-
where(Sequel[:jobs][:state] => [PollableJobModel::POLLING_STATE, PollableJobModel::PROCESSING_STATE]).
126-
where(Sequel[:delayed_jobs][:failed_at] => nil).
127-
where(Sequel[:jobs][:resource_guid] => Sequel[instance_table][:guid]).
128-
exists
129-
end
130-
131107
def logger
132108
@logger ||= Steno.logger('cc.background.service-operations-create-in-progress-cleanup')
133109
end

app/jobs/runtime/service_operations_delete_stuck_in_progress_retry.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ def max_attempts
1818
def retry_stuck(operation_model, instance_model, foreign_key, jobs_operation)
1919
# Find stuck service instance 'delete' operations where the broker may still be working
2020
# but CC's polling job has permanently failed due to a transient error (e.g. brief db connection flip).
21-
#
22-
# Unlike create/update we do not mark the operation failed: for a delete we re-enqueue the original
23-
# polling job so the deprovision is driven to completion. The original delayed_job's serialized handler
24-
# is reused, preserving @start_time so the ReoccurringJob max-duration expiry (which marks the operation
25-
# failed via handle_timeout) still fires against the original polling window.
21+
# We re-enqueue the original polling job so the deprovision is driven to completion. The original
22+
# delayed_job's serialized handler is reused, preserving @start_time so the ReoccuringJob max-duration expiry
23+
# (which marks the operation failed via handle_timeout) still fires against the original polling window.
2624
operation_table = operation_model.table_name
2725
instance_table = instance_model.table_name
2826

@@ -78,9 +76,8 @@ def resolve_stuck(operation_model, instance_model, op_id, resource_id, pollable_
7876
end
7977
end
8078

81-
# Reuse the original delete polling job by deserializing the failed delayed_job's handler and unwrapping
82-
# the wrapper chain (LoggingContextJob → TimeoutJob → PollableJobWrapper → DeleteServiceInstanceJob).
83-
# This preserves the original @user_audit_info, @start_time and the recursive-vs-plain delete variant.
79+
# Reuse the original delete polling job by deserializing the failed delayed_job's handler and unwrapping the
80+
# wrapper chain. This preserves @user_audit_info, @start_time and the recursive-vs-plain delete variant.
8481
def deserialize_handler(pollable)
8582
delayed_job = Delayed::Job[guid: pollable.delayed_job_guid]
8683
return unless delayed_job

spec/unit/jobs/runtime/service_operations_create_in_progress_cleanup_spec.rb

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,6 @@ def prepare_stuck_service_instance(
5555
{ service_instance: service_instance, pjob: pjob, delayed_job: dj }
5656
end
5757

58-
# Attach an additional live pollable job (POLLING/PROCESSING, delayed_job NOT failed)
59-
# for the same instance + operation. Mirrors a second create actively polling while a
60-
# stale, permanently-failed pollable from a previous attempt lingers.
61-
def add_live_pollable(service_instance, operation: 'service_instance.create', state: PollableJobModel::POLLING_STATE)
62-
dj = Delayed::Job.create!(
63-
guid: SecureRandom.uuid,
64-
handler: 'fake',
65-
run_at: Time.now,
66-
queue: 'cc-generic'
67-
)
68-
create(:pollable_job_model,
69-
state: state,
70-
operation: operation,
71-
resource_guid: service_instance.guid,
72-
resource_type: 'service_instances',
73-
delayed_job_guid: dj.guid)
74-
end
75-
7658
shared_examples 'does not trigger orphan mitigation' do
7759
before { job.perform }
7860

0 commit comments

Comments
 (0)