Skip to content

Commit 1aa5272

Browse files
committed
Cover dataclip search_vector budget-exhaustion path
Mirror the log_lines fix: make DataclipSearchVectorWorker batch_size/max_batches configurable through the Lightning.Config seam (defaults 250/10 in config.exs, 2/2 in test.exs), restructure drain/2 into drain/4, and add a test exercising the recursive drain, budget guard, and snowball enqueue.
1 parent 771a8a7 commit 1aa5272

5 files changed

Lines changed: 68 additions & 21 deletions

File tree

config/config.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ config :lightning, :claim_work_mem, nil
189189

190190
config :lightning, :log_lines_search_indexing, batch_size: 2_500, max_batches: 10
191191

192+
config :lightning, :dataclip_search_indexing, batch_size: 250, max_batches: 10
193+
192194
config :lightning, Lightning.Runtime.RuntimeManager, start: false
193195

194196
config :lightning, LightningWeb.CollectionsController,

config/test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ config :lightning, :is_resettable_demo, true
137137
# the per-run budget guard, exercising the snowball follow-up path.
138138
config :lightning, :log_lines_search_indexing, batch_size: 2, max_batches: 2
139139

140+
config :lightning, :dataclip_search_indexing, batch_size: 2, max_batches: 2
141+
140142
config :lightning, :github_app,
141143
app_id: "111111",
142144
app_name: "test-github",

lib/lightning/config.ex

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ defmodule Lightning.Config do
111111
Application.get_env(:lightning, :log_lines_search_indexing, [])
112112
end
113113

114+
@impl true
115+
def dataclip_search_indexing_batch_size do
116+
dataclip_search_indexing_config() |> Keyword.fetch!(:batch_size)
117+
end
118+
119+
@impl true
120+
def dataclip_search_indexing_max_batches do
121+
dataclip_search_indexing_config() |> Keyword.fetch!(:max_batches)
122+
end
123+
124+
defp dataclip_search_indexing_config do
125+
Application.get_env(:lightning, :dataclip_search_indexing, [])
126+
end
127+
114128
@impl true
115129
def default_ecto_database_timeout do
116130
Application.get_env(:lightning, Lightning.Repo) |> Keyword.get(:timeout)
@@ -499,6 +513,8 @@ defmodule Lightning.Config do
499513
@callback activity_cleanup_chunk_size() :: integer()
500514
@callback log_lines_search_indexing_batch_size() :: pos_integer()
501515
@callback log_lines_search_indexing_max_batches() :: pos_integer()
516+
@callback dataclip_search_indexing_batch_size() :: pos_integer()
517+
@callback dataclip_search_indexing_max_batches() :: pos_integer()
502518
@callback default_ecto_database_timeout() :: integer()
503519
@callback repo_connection_token_signer() :: Joken.Signer.t()
504520
@callback reset_password_token_validity_in_days() :: integer()
@@ -621,6 +637,14 @@ defmodule Lightning.Config do
621637
impl().log_lines_search_indexing_max_batches()
622638
end
623639

640+
def dataclip_search_indexing_batch_size do
641+
impl().dataclip_search_indexing_batch_size()
642+
end
643+
644+
def dataclip_search_indexing_max_batches do
645+
impl().dataclip_search_indexing_max_batches()
646+
end
647+
624648
def default_ecto_database_timeout do
625649
impl().default_ecto_database_timeout()
626650
end

lib/lightning/invocation/dataclip_search_vector_worker.ex

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorker do
1717
`english_nostop` config to match the read side (`Lightning.Invocation`), which
1818
queries with `to_tsquery('english_nostop', ...)`.
1919
20-
Each run drains pending rows newest-first, in batches of `@batch_size` up to
21-
`@max_batches` per run. A run that exhausts its budget leaves backlog behind
20+
Each run drains pending rows newest-first, in batches up to a per-run budget
21+
(batch size and max batches are configurable via `Lightning.Config`). A run
22+
that exhausts its budget leaves backlog behind
2223
and enqueues an immediate follow-up ("snowball"); otherwise the minute-ly cron
2324
tick keeps pace. The worker shares the `search_indexing` queue with
2425
`Lightning.LogLines.SearchVectorWorker`; that queue runs at concurrency 2, so
@@ -40,10 +41,6 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorker do
4041

4142
require Logger
4243

43-
@batch_size 250
44-
# Per-run budget.
45-
@max_batches 10
46-
4744
@drain_sql """
4845
WITH pending AS (
4946
SELECT id FROM dataclips
@@ -59,7 +56,10 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorker do
5956

6057
@impl Oban.Worker
6158
def perform(%Oban.Job{}) do
62-
{filled, budget_exhausted?} = drain(0, 0)
59+
batch_size = Lightning.Config.dataclip_search_indexing_batch_size()
60+
max_batches = Lightning.Config.dataclip_search_indexing_max_batches()
61+
62+
{filled, budget_exhausted?} = drain(0, 0, batch_size, max_batches)
6363

6464
Logger.info(fn ->
6565
# coveralls-ignore-start
@@ -76,20 +76,20 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorker do
7676
{:ok, filled}
7777
end
7878

79-
# Drains up to @max_batches batches, accumulating the number of rows filled.
79+
# Drains up to max_batches batches, accumulating the number of rows filled.
8080
# Returns {filled, budget_exhausted?}. Stops early when a batch fills fewer
81-
# than @batch_size rows (backlog drained).
82-
defp drain(filled, batches) when batches >= @max_batches do
83-
{filled, true}
84-
end
85-
86-
defp drain(filled, batches) do
87-
%{num_rows: num_rows} = Repo.query!(@drain_sql, [@batch_size])
88-
89-
if num_rows < @batch_size do
90-
{filled + num_rows, false}
81+
# than batch_size rows (backlog drained).
82+
defp drain(filled, batches, batch_size, max_batches) do
83+
if batches >= max_batches do
84+
{filled, true}
9185
else
92-
drain(filled + num_rows, batches + 1)
86+
%{num_rows: num_rows} = Repo.query!(@drain_sql, [batch_size])
87+
88+
if num_rows < batch_size do
89+
{filled + num_rows, false}
90+
else
91+
drain(filled + num_rows, batches + 1, batch_size, max_batches)
92+
end
9393
end
9494
end
9595
end

test/lightning/invocation/dataclip_search_vector_worker_test.exs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,11 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorkerTest do
9595
end
9696

9797
test "drains all pending dataclips across a batch" do
98+
# Stay within the tiny test budget (batch_size: 2, max_batches: 2) so a
99+
# single run drains everything: batch 1 fills 2, batch 2 fills 1 (< 2) and
100+
# stops without tripping the budget.
98101
dataclips =
99-
for n <- 1..5 do
102+
for n <- 1..3 do
100103
insert(:dataclip, body: %{"n" => "draindataclip#{n}"})
101104
end
102105

@@ -105,7 +108,7 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorkerTest do
105108
search_vector_state(dataclip.id, "draindataclip1")
106109
end
107110

108-
assert {:ok, 5} = perform_job(DataclipSearchVectorWorker, %{})
111+
assert {:ok, 3} = perform_job(DataclipSearchVectorWorker, %{})
109112

110113
for dataclip <- dataclips do
111114
assert %{null?: false} =
@@ -123,6 +126,22 @@ defmodule Lightning.Invocation.DataclipSearchVectorWorkerTest do
123126
end)
124127
end
125128

129+
test "snowballs an immediate follow-up when the per-run budget is exhausted" do
130+
# config/test.exs sets batch_size: 2, max_batches: 2. With 5 pending rows
131+
# the run fills two full batches (4 rows), reaches max_batches, and trips
132+
# the budget guard, leaving backlog behind and enqueuing a snowball.
133+
for n <- 1..5, do: insert(:dataclip, body: %{"n" => "overflow#{n}"})
134+
135+
Oban.Testing.with_testing_mode(:manual, fn ->
136+
assert {:ok, 4} = perform_job(DataclipSearchVectorWorker, %{})
137+
138+
assert_enqueued(
139+
worker: DataclipSearchVectorWorker,
140+
args: %{"trigger" => "snowball"}
141+
)
142+
end)
143+
end
144+
126145
test "is idempotent: a second run with nothing pending fills 0 and snowballs nothing" do
127146
for n <- 1..3, do: insert(:dataclip, body: %{"n" => "again#{n}"})
128147

0 commit comments

Comments
 (0)