switchman-inst-jobs 3.2.5 → 3.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d62495f21f6c28621f0a8f4332f2edd453c7e14966521a8a14bc85e42065e7f7
4
- data.tar.gz: e77bb0b5a8caa21619abfcf2873cb3a8e5eb5775328a0b314fc522ab97672159
3
+ metadata.gz: fc596dd957a48e77a57106b08a3474fa39b2127f4c2a929fd363b3208e2a2e59
4
+ data.tar.gz: 39157783192cf59506d320ec36e7426e2239dd6de248a32c60cfcbf8eedf667e
5
5
  SHA512:
6
- metadata.gz: 778759b4e3106fd2f7ce6748bcf61c2643a192d202412565f640985abcb58c1b5401e19f5d48907f12a37e4f203a20314afd727de5cfca14328bb0d9c5c13103
7
- data.tar.gz: 42349259d9d2643e1ba833aef0651199da0b08f9d5567346e4d2748e652f0d4830d7a22d9fac925b46320a1e3bb639b35514dccd76e547900d5edfc333b365c8
6
+ metadata.gz: 5b256e4ac3fe9a779faada8b57df4725f1a227af4fddff925277ca5b68fa476ec5fd51bbbf96ff996e393089d162f41c043a3225587a79e4df4096d9ecdc4168
7
+ data.tar.gz: 5b43c7df21c598d1e9838e62e982ebe1ec31ab7a3daafa7b48bfafb882f3b716ced376eca1b2b80c27895e58a04bbd684fcfbed437712efcb20aa5d5ef780d55
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UpdateAfterDeleteTriggerForSingletonTransitionCases < ActiveRecord::Migration[5.2]
4
+ def up
5
+ execute(<<~SQL)
6
+ CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
7
+ DECLARE
8
+ next_strand varchar;
9
+ running_count integer;
10
+ should_lock boolean;
11
+ should_be_precise boolean;
12
+ update_query varchar;
13
+ skip_locked varchar;
14
+ transition boolean;
15
+ BEGIN
16
+ IF OLD.strand IS NOT NULL THEN
17
+ should_lock := true;
18
+ should_be_precise := OLD.id % (OLD.max_concurrent * 4) = 0;
19
+
20
+ IF NOT should_be_precise AND OLD.max_concurrent > 16 THEN
21
+ running_count := (SELECT COUNT(*) FROM (
22
+ SELECT 1 as one FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
23
+ ) subquery_for_count);
24
+ should_lock := running_count < OLD.max_concurrent;
25
+ END IF;
26
+
27
+ IF should_lock THEN
28
+ PERFORM pg_advisory_xact_lock(half_md5_as_bigint(OLD.strand));
29
+ END IF;
30
+
31
+ -- note that we don't really care if the row we're deleting has a singleton, or if it even
32
+ -- matches the row(s) we're going to update. we just need to make sure that whatever
33
+ -- singleton we grab isn't already running (which is a simple existence check, since
34
+ -- the unique indexes ensure there is at most one singleton running, and one queued)
35
+ update_query := 'UPDATE delayed_jobs SET next_in_strand=true WHERE id IN (
36
+ SELECT id FROM delayed_jobs j2
37
+ WHERE next_in_strand=false AND
38
+ j2.strand=$1.strand AND
39
+ (j2.singleton IS NULL OR NOT EXISTS (SELECT 1 FROM delayed_jobs j3 WHERE j3.singleton=j2.singleton AND j3.id<>j2.id AND (j3.locked_by IS NULL OR j3.locked_by IS NOT NULL)))
40
+ ORDER BY j2.strand_order_override ASC, j2.id ASC
41
+ LIMIT ';
42
+
43
+ IF should_be_precise THEN
44
+ running_count := (SELECT COUNT(*) FROM (
45
+ SELECT 1 FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
46
+ ) s);
47
+ IF running_count < OLD.max_concurrent THEN
48
+ update_query := update_query || '($1.max_concurrent - $2)';
49
+ ELSE
50
+ -- we have too many running already; just bail
51
+ RETURN OLD;
52
+ END IF;
53
+ ELSE
54
+ update_query := update_query || '1';
55
+
56
+ -- n-strands don't require precise ordering; we can make this query more performant
57
+ IF OLD.max_concurrent > 1 THEN
58
+ skip_locked := ' SKIP LOCKED';
59
+ END IF;
60
+ END IF;
61
+
62
+ update_query := update_query || ' FOR UPDATE' || COALESCE(skip_locked, '') || ')';
63
+ EXECUTE update_query USING OLD, running_count;
64
+ END IF;
65
+
66
+ IF OLD.singleton IS NOT NULL THEN
67
+ transition := EXISTS (SELECT 1 FROM delayed_jobs AS j1 WHERE j1.singleton = OLD.singleton AND j1.strand IS DISTINCT FROM OLD.strand AND locked_by IS NULL);
68
+
69
+ IF transition THEN
70
+ next_strand := (SELECT j1.strand FROM delayed_jobs AS j1 WHERE j1.singleton = OLD.singleton AND j1.strand IS DISTINCT FROM OLD.strand AND locked_by IS NULL AND j1.strand IS NOT NULL LIMIT 1);
71
+
72
+ IF next_strand IS NOT NULL THEN
73
+ -- if the singleton has a new strand defined, we need to lock it to ensure we obey n_strand constraints --
74
+ IF NOT pg_try_advisory_xact_lock(half_md5_as_bigint(next_strand)) THEN
75
+ -- a failure to acquire the lock means that another process already has it and will thus handle this singleton --
76
+ RETURN OLD;
77
+ END IF;
78
+ END IF;
79
+ ELSIF OLD.strand IS NOT NULL THEN
80
+ -- if there is no transition and there is a strand then we have already handled this singleton in the case above --
81
+ RETURN OLD;
82
+ END IF;
83
+
84
+ -- handles transitioning a singleton from stranded to not stranded --
85
+ -- handles transitioning a singleton from unstranded to stranded --
86
+ -- handles transitioning a singleton from strand A to strand B --
87
+ -- these transitions are a relatively rare case, so we take a shortcut and --
88
+ -- only start the next singleton if its strand does not currently have any running jobs --
89
+ -- if it does, the next stranded job that finishes will start this singleton if it can --
90
+ UPDATE delayed_jobs SET next_in_strand=true WHERE id IN (
91
+ SELECT id FROM delayed_jobs j2
92
+ WHERE next_in_strand=false AND
93
+ j2.singleton=OLD.singleton AND
94
+ j2.locked_by IS NULL AND
95
+ (j2.strand IS NULL OR NOT EXISTS (SELECT 1 FROM delayed_jobs j3 WHERE j3.strand=j2.strand AND j3.id<>j2.id))
96
+ FOR UPDATE
97
+ );
98
+ END IF;
99
+ RETURN OLD;
100
+ END;
101
+ $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
102
+ SQL
103
+ end
104
+
105
+ def down
106
+ execute(<<~SQL)
107
+ CREATE OR REPLACE FUNCTION #{connection.quote_table_name('delayed_jobs_after_delete_row_tr_fn')} () RETURNS trigger AS $$
108
+ DECLARE
109
+ running_count integer;
110
+ should_lock boolean;
111
+ should_be_precise boolean;
112
+ update_query varchar;
113
+ skip_locked varchar;
114
+ BEGIN
115
+ IF OLD.strand IS NOT NULL THEN
116
+ should_lock := true;
117
+ should_be_precise := OLD.id % (OLD.max_concurrent * 4) = 0;
118
+
119
+ IF NOT should_be_precise AND OLD.max_concurrent > 16 THEN
120
+ running_count := (SELECT COUNT(*) FROM (
121
+ SELECT 1 as one FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
122
+ ) subquery_for_count);
123
+ should_lock := running_count < OLD.max_concurrent;
124
+ END IF;
125
+
126
+ IF should_lock THEN
127
+ PERFORM pg_advisory_xact_lock(half_md5_as_bigint(OLD.strand));
128
+ END IF;
129
+
130
+ -- note that we don't really care if the row we're deleting has a singleton, or if it even
131
+ -- matches the row(s) we're going to update. we just need to make sure that whatever
132
+ -- singleton we grab isn't already running (which is a simple existence check, since
133
+ -- the unique indexes ensure there is at most one singleton running, and one queued)
134
+ update_query := 'UPDATE delayed_jobs SET next_in_strand=true WHERE id IN (
135
+ SELECT id FROM delayed_jobs j2
136
+ WHERE next_in_strand=false AND
137
+ j2.strand=$1.strand AND
138
+ (j2.singleton IS NULL OR NOT EXISTS (SELECT 1 FROM delayed_jobs j3 WHERE j3.singleton=j2.singleton AND j3.id<>j2.id AND (j3.locked_by IS NULL OR j3.locked_by IS NOT NULL)))
139
+ ORDER BY j2.strand_order_override ASC, j2.id ASC
140
+ LIMIT ';
141
+
142
+ IF should_be_precise THEN
143
+ running_count := (SELECT COUNT(*) FROM (
144
+ SELECT 1 FROM delayed_jobs WHERE strand = OLD.strand AND next_in_strand = 't' LIMIT OLD.max_concurrent
145
+ ) s);
146
+ IF running_count < OLD.max_concurrent THEN
147
+ update_query := update_query || '($1.max_concurrent - $2)';
148
+ ELSE
149
+ -- we have too many running already; just bail
150
+ RETURN OLD;
151
+ END IF;
152
+ ELSE
153
+ update_query := update_query || '1';
154
+
155
+ -- n-strands don't require precise ordering; we can make this query more performant
156
+ IF OLD.max_concurrent > 1 THEN
157
+ skip_locked := ' SKIP LOCKED';
158
+ END IF;
159
+ END IF;
160
+
161
+ update_query := update_query || ' FOR UPDATE' || COALESCE(skip_locked, '') || ')';
162
+ EXECUTE update_query USING OLD, running_count;
163
+ ELSIF OLD.singleton IS NOT NULL THEN
164
+ UPDATE delayed_jobs SET next_in_strand = 't' WHERE singleton=OLD.singleton AND next_in_strand=false AND locked_by IS NULL;
165
+ END IF;
166
+ RETURN OLD;
167
+ END;
168
+ $$ LANGUAGE plpgsql SET search_path TO #{::Switchman::Shard.current.name};
169
+ SQL
170
+ end
171
+ end
@@ -1,3 +1,3 @@
1
1
  module SwitchmanInstJobs
2
- VERSION = '3.2.5'.freeze
2
+ VERSION = '3.2.6'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman-inst-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.5
4
+ version: 3.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Petty
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-08 00:00:00.000000000 Z
11
+ date: 2021-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inst-jobs
@@ -260,7 +260,7 @@ dependencies:
260
260
  - - "~>"
261
261
  - !ruby/object:Gem::Version
262
262
  version: '1.4'
263
- description:
263
+ description:
264
264
  email:
265
265
  - bpetty@instructure.com
266
266
  executables: []
@@ -302,6 +302,7 @@ files:
302
302
  - db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb
303
303
  - db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb
304
304
  - db/migrate/20211101190934_update_after_delete_trigger_for_singleton_index.rb
305
+ - db/migrate/20211207094200_update_after_delete_trigger_for_singleton_transition_cases.rb
305
306
  - lib/switchman-inst-jobs.rb
306
307
  - lib/switchman_inst_jobs.rb
307
308
  - lib/switchman_inst_jobs/active_record/connection_adapters/postgresql_adapter.rb
@@ -325,8 +326,10 @@ files:
325
326
  homepage: https://github.com/instructure/switchman-inst-jobs
326
327
  licenses:
327
328
  - MIT
328
- metadata: {}
329
- post_install_message:
329
+ metadata:
330
+ allowed_push_host: https://rubygems.org
331
+ rubygems_mfa_required: 'true'
332
+ post_install_message:
330
333
  rdoc_options: []
331
334
  require_paths:
332
335
  - lib
@@ -341,8 +344,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
341
344
  - !ruby/object:Gem::Version
342
345
  version: '0'
343
346
  requirements: []
344
- rubygems_version: 3.2.24
345
- signing_key:
347
+ rubygems_version: 3.1.4
348
+ signing_key:
346
349
  specification_version: 4
347
350
  summary: Switchman and Instructure Jobs compatibility gem.
348
351
  test_files: []