worker_plugins 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/app/services/worker_plugins/delete_orphan_links.rb +39 -0
- data/app/services/worker_plugins/query_links_status.rb +19 -39
- data/lib/worker_plugins/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0724aebdce87819ebafc787c475284e25e7a6ee31837040e8532b0c400c95db3
|
|
4
|
+
data.tar.gz: dc8cd4edddcdce80d22c2e3c10990f4fa966f112fe67f01c1bd58443c313079b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f58a6c6a65122966a57b948f78758761825e2eed97bb13fb4277b627ec4db11bade6856b2baf23e1f1056fb7169d545bfeaf6d565bbbddbb027bfb61c9d16f26
|
|
7
|
+
data.tar.gz: a4fb5385013ab116c856bd5b5986ce04bcccbfb18b342edb41f463d652e664ecdd0b01567e75cf321f0299d64d8adfff5b37c9d2ef2ed75f81da31286975884a
|
data/README.md
CHANGED
|
@@ -67,6 +67,19 @@ end
|
|
|
67
67
|
|
|
68
68
|
and schedule `Workplaces::DeleteOld` instead.
|
|
69
69
|
|
|
70
|
+
## Scheduled cleanup of orphan links
|
|
71
|
+
|
|
72
|
+
`WorkerPlugins::DeleteOrphanLinks` removes `worker_plugins_workplace_links` whose target row no longer exists — i.e. links that point at a resource that was destroyed without the link being cleaned up alongside. Run it periodically from a background job to keep the links table consistent and keep probes like `QueryLinksStatus#checked_count` honest.
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
result = WorkerPlugins::DeleteOrphanLinks.execute!
|
|
76
|
+
# => {deleted_count: <N>}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Links whose `resource_type` doesn't resolve to a Ruby class (e.g. a model was renamed or removed) are left alone — cleaning those up requires human judgement.
|
|
80
|
+
|
|
81
|
+
Schedule the same way as `DeleteOldWorkplaces` — typically once a day off-hours.
|
|
82
|
+
|
|
70
83
|
## Release
|
|
71
84
|
|
|
72
85
|
Run the release task from a clean worktree:
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class WorkerPlugins::DeleteOrphanLinks < WorkerPlugins::ApplicationService
|
|
2
|
+
# Deletes `worker_plugins_workplace_links` whose target row no longer
|
|
3
|
+
# exists — i.e. links pointing at a resource that was destroyed without
|
|
4
|
+
# the link being cleaned up alongside. Intended to be scheduled by the
|
|
5
|
+
# consumer application from a background job.
|
|
6
|
+
#
|
|
7
|
+
# Links whose `resource_type` doesn't resolve to a Ruby class (e.g. a
|
|
8
|
+
# model was renamed or removed) are left alone — cleaning those up is
|
|
9
|
+
# a separate concern that requires human judgement.
|
|
10
|
+
def perform
|
|
11
|
+
deleted_count = distinct_resource_types.sum do |resource_type|
|
|
12
|
+
delete_orphans_for(resource_type)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
succeed!(deleted_count:)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def distinct_resource_types
|
|
19
|
+
WorkerPlugins::WorkplaceLink.distinct.pluck(:resource_type)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete_orphans_for(resource_type)
|
|
23
|
+
model_class = resource_type.safe_constantize
|
|
24
|
+
return 0 unless model_class
|
|
25
|
+
|
|
26
|
+
WorkerPlugins::WorkplaceLink
|
|
27
|
+
.where(resource_type:)
|
|
28
|
+
.where.not(resource_id: live_ids_query(model_class))
|
|
29
|
+
.delete_all
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def live_ids_query(model_class)
|
|
33
|
+
WorkerPlugins::SelectColumnWithTypeCast.execute!(
|
|
34
|
+
column_name_to_select: model_class.primary_key,
|
|
35
|
+
column_to_compare_with: WorkerPlugins::WorkplaceLink.column_for_attribute(:resource_id),
|
|
36
|
+
query: model_class.all
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -3,7 +3,7 @@ class WorkerPlugins::QueryLinksStatus < WorkerPlugins::ApplicationService
|
|
|
3
3
|
|
|
4
4
|
def perform
|
|
5
5
|
query_count = query.count
|
|
6
|
-
checked_count = count_linked_rows
|
|
6
|
+
checked_count = count_linked_rows(query_count)
|
|
7
7
|
|
|
8
8
|
succeed!(
|
|
9
9
|
all_checked: query_count == checked_count,
|
|
@@ -13,46 +13,26 @@ class WorkerPlugins::QueryLinksStatus < WorkerPlugins::ApplicationService
|
|
|
13
13
|
)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def count_linked_rows
|
|
16
|
+
def count_linked_rows(query_count)
|
|
17
17
|
base_scope = workplace.workplace_links.where(resource_type: query.klass.name)
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
resource_id_column = "#{quote_table(WorkerPlugins::WorkplaceLink.table_name)}.#{quote_column(:resource_id)}"
|
|
37
|
-
|
|
38
|
-
"INNER JOIN #{target_table} ON #{target_pk} = #{resource_id_expression_for_join(resource_id_column)}"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# On MySQL / MariaDB and SQLite, implicit conversion handles comparing the
|
|
42
|
-
# target's primary key against the VARCHAR `resource_id` column. Postgres
|
|
43
|
-
# is strict about types and needs an explicit cast when they differ.
|
|
44
|
-
def resource_id_expression_for_join(resource_id_column)
|
|
45
|
-
return resource_id_column unless postgres?
|
|
46
|
-
|
|
47
|
-
target_pk_type = query.klass.column_for_attribute(query.klass.primary_key).type
|
|
48
|
-
resource_id_type = WorkerPlugins::WorkplaceLink.column_for_attribute(:resource_id).type
|
|
49
|
-
|
|
50
|
-
return resource_id_column if target_pk_type == resource_id_type
|
|
51
|
-
|
|
52
|
-
case target_pk_type
|
|
53
|
-
when :uuid then "CAST(#{resource_id_column} AS UUID)"
|
|
54
|
-
when :integer then "CAST(#{resource_id_column} AS BIGINT)"
|
|
55
|
-
else resource_id_column
|
|
19
|
+
# Fast path for unscoped queries: a plain index-only COUNT against the
|
|
20
|
+
# `(workplace_id, resource_type, resource_id)` composite index resolves in
|
|
21
|
+
# ~50 ms even for workplaces with hundreds of thousands of links. Joining
|
|
22
|
+
# back to the target table to exclude orphaned links (whose resource row
|
|
23
|
+
# has since been destroyed) would take 10+ seconds on the same data
|
|
24
|
+
# regardless of which shape we pick — the DB still has to cross-reference
|
|
25
|
+
# every link against the target's primary key. Instead we clamp the raw
|
|
26
|
+
# count to `query_count`; `WorkerPlugins::DeleteOrphanLinks` (scheduled
|
|
27
|
+
# daily by consumers) keeps the orphan count at zero so the raw count
|
|
28
|
+
# equals the live-linked count in practice. When orphans do briefly exist
|
|
29
|
+
# between cleanup runs, clamping bounds the over-count at the query's
|
|
30
|
+
# total — `all_checked` / `some_checked` stay correct because they're
|
|
31
|
+
# computed off the clamped value.
|
|
32
|
+
if relation_unscoped?(query)
|
|
33
|
+
[base_scope.count, query_count].min
|
|
34
|
+
else
|
|
35
|
+
base_scope.where(resource_id: query_with_selected_ids).count
|
|
56
36
|
end
|
|
57
37
|
end
|
|
58
38
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: worker_plugins
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kasper Stöckel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Rails framework for easily choosing and creating lists of objects and
|
|
14
14
|
execute plugins against them.
|
|
@@ -27,6 +27,7 @@ files:
|
|
|
27
27
|
- app/services/worker_plugins/add_query.rb
|
|
28
28
|
- app/services/worker_plugins/application_service.rb
|
|
29
29
|
- app/services/worker_plugins/delete_old_workplaces.rb
|
|
30
|
+
- app/services/worker_plugins/delete_orphan_links.rb
|
|
30
31
|
- app/services/worker_plugins/query_links_status.rb
|
|
31
32
|
- app/services/worker_plugins/remove_query.rb
|
|
32
33
|
- app/services/worker_plugins/select_column_with_type_cast.rb
|