Class: Wp2txt::CorpusJobManager
- Inherits:
-
Object
- Object
- Wp2txt::CorpusJobManager
- Defined in:
- lib/wp2txt/corpus_jobs.rb
Overview
In-process job manager for long-running corpus extractions (MCP server). Each job runs in its own thread with its own Corpus instance (separate SQLite connections and reader), so it never contends with the main request thread. Jobs are lost when the server exits (documented v1 limit).
Instance Method Summary collapse
-
#cancel(job_id) ⇒ Object
Request cancellation; takes effect at the next batch boundary.
-
#initialize(corpus_factory) ⇒ CorpusJobManager
constructor
A new instance of CorpusJobManager.
- #list ⇒ Object
-
#start_extract(params) ⇒ Hash
Start an extraction job.
-
#status(job_id) ⇒ Hash?
Public job state (no thread object), nil if unknown.
Constructor Details
#initialize(corpus_factory) ⇒ CorpusJobManager
Returns a new instance of CorpusJobManager.
13 14 15 16 17 18 |
# File 'lib/wp2txt/corpus_jobs.rb', line 13 def initialize(corpus_factory) @factory = corpus_factory @jobs = {} @mutex = Mutex.new @seq = 0 end |
Instance Method Details
#cancel(job_id) ⇒ Object
Request cancellation; takes effect at the next batch boundary
73 74 75 76 77 78 79 |
# File 'lib/wp2txt/corpus_jobs.rb', line 73 def cancel(job_id) state = read(job_id) return nil unless state update(job_id) { |s| s[:cancel] = true } { job_id: job_id, status: state[:status], cancel_requested: true } end |
#list ⇒ Object
81 82 83 |
# File 'lib/wp2txt/corpus_jobs.rb', line 81 def list @mutex.synchronize { @jobs.values.map { |s| public_view(s) } } end |
#start_extract(params) ⇒ Hash
Start an extraction job. params are Corpus#extract_corpus keywords. Only one job runs at a time: each job forks worker processes, and unbounded concurrent jobs would multiply workers against the same dump.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/wp2txt/corpus_jobs.rb', line 24 def start_extract(params) running = @mutex.synchronize { @jobs.values.find { |s| s[:status] == "running" } } if running return { error: "another job is already running (#{running[:job_id]}); " \ "poll job_status or cancel_job before starting a new one" } end job_id = @mutex.synchronize { format("job-%04d", @seq += 1) } state = { job_id: job_id, status: "running", started_at: Time.now.utc.iso8601, params: params, titles_done: 0, titles_total: nil, cancel: false } @mutex.synchronize { @jobs[job_id] = state } thread = Thread.new do corpus = @factory.call begin result = corpus.extract_corpus( **params, max_articles: nil, progress: lambda { |done, total| update(job_id) { |s| s[:titles_done] = done; s[:titles_total] = total } }, cancel_check: -> { read(job_id)[:cancel] } ) update(job_id) { |s| s[:status] = "completed"; s[:result] = result; s[:finished_at] = Time.now.utc.iso8601 } rescue Corpus::Cancelled update(job_id) { |s| s[:status] = "cancelled"; s[:finished_at] = Time.now.utc.iso8601 } rescue StandardError => e update(job_id) { |s| s[:status] = "error"; s[:error] = "#{e.class}: #{e.}"; s[:finished_at] = Time.now.utc.iso8601 } ensure corpus.close end end thread.report_on_exception = false update(job_id) { |s| s[:thread] = thread } { job_id: job_id, status: "running" } end |
#status(job_id) ⇒ Hash?
Returns public job state (no thread object), nil if unknown.
65 66 67 68 69 70 |
# File 'lib/wp2txt/corpus_jobs.rb', line 65 def status(job_id) state = read(job_id) return nil unless state public_view(state) end |