Class: Wurk::Web::Search

Inherits:
Object
  • Object
show all
Defined in:
lib/wurk/web/search.rb

Overview

Pro feature parity: substring search across queues / retries / scheduled / dead. Spec: docs/target/sidekiq-pro.md §10.1 ("Search box on Retry/Scheduled/Dead pages, substring across job payload via ZSCAN"). Wurk ships it free, extended to also cover the queue LIST.

ZSET stores are ZSCAN-paged and filtered client-side against the literal substring — the same shape as the queue LIST's paged LRANGE filter — rather than ZSCAN MATCH: MATCH returns only matches, never the count of elements Redis walked, so a no-match keystroke couldn't be metered and could round-trip an enormous ZSET. Both paths charge the shared scan budget so a keystroke never full-walks a multi-million-entry store.

Result shape mirrors Wurk::Api::Serializers#sorted_entry so the SPA renders search hits with the same component as a sorted-set row.

Constant Summary collapse

DEFAULT_LIMIT =
100
MAX_LIMIT =
500
KINDS =
%w[queues retry scheduled dead].freeze
QUEUE_PAGE =
50
ZSCAN_PAGE =
200
SCAN_LIMIT_PER_QUEUE =

A queue LIST has no SCAN, so a keystroke must never full-walk a multi-million-entry queue. Bound each queue's scan and the whole request's scan; truncated? flips when we stop early so the API can tell the UI it's showing partial results.

5_000
SCAN_BUDGET =
20_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(substring, kinds: KINDS, limit: DEFAULT_LIMIT) ⇒ Search

Returns a new instance of Search.



39
40
41
42
43
44
45
46
# File 'lib/wurk/web/search.rb', line 39

def initialize(substring, kinds: KINDS, limit: DEFAULT_LIMIT)
  @substring = substring.to_s
  @kinds = (Array(kinds).map(&:to_s) & KINDS)
  @kinds = KINDS.dup if @kinds.empty?
  @limit = limit.to_i.clamp(1, MAX_LIMIT)
  @truncated = false
  @scanned = 0
end

Instance Attribute Details

#kindsObject (readonly)

Returns the value of attribute kinds.



37
38
39
# File 'lib/wurk/web/search.rb', line 37

def kinds
  @kinds
end

#limitObject (readonly)

Returns the value of attribute limit.



37
38
39
# File 'lib/wurk/web/search.rb', line 37

def limit
  @limit
end

#substringObject (readonly)

Returns the value of attribute substring.



37
38
39
# File 'lib/wurk/web/search.rb', line 37

def substring
  @substring
end

Instance Method Details

#eachObject

Streams matching hits across every selected store. Stops at limit. Yields Hashes shaped like sorted_entry payloads with an extra :kind discriminator + :name (queue name or set name).



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/wurk/web/search.rb', line 57

def each(&)
  return enum_for(:each) unless block_given?
  return if @substring.empty?

  @scanned = 0
  @truncated = false
  emitted = 0
  each_hit do |row|
    yield row
    emitted += 1
    break if emitted >= @limit
  end
end

#to_aObject



71
72
73
# File 'lib/wurk/web/search.rb', line 71

def to_a
  each.to_a
end

#truncated?Boolean

True when a scan stopped at a bound (a queue's per-queue cap, a sorted set's page loop, or the shared per-request budget) with elements left unexamined — the hit list may be incomplete. Meaningful only after each/to_a has driven the scan.

Returns:

  • (Boolean)


52
# File 'lib/wurk/web/search.rb', line 52

def truncated? = @truncated