umputun / umputun/newscope

With extraction disabled, an item that fails classification is never retried

Open
#48 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
53
Forks
7
PR merge metrics
No merged PRs in 30d

Description

When extraction.enabled is false, an item that reaches the database but fails classification is never picked up again: neither startup loop can return it, and the feed update path skips it as already seen. Line references are against e220a28.

Mechanism

processExistingItems is the only catch-up path, and both of its loops exclude these rows:

  • pkg/scheduler/scheduler.go:335-341: the extraction backlog loop breaks immediately when extraction is disabled, because nothing marks such items extracted and GetItemsNeedingExtraction would keep returning them.
  • pkg/repository/item.go:171-179: the second loop uses GetUnclassifiedItems, whose SQL requires extracted_content != ''.
  • With extraction disabled extractContent returns before touching the database, and ProcessBatch passes a nil extraction to UpdateItemProcessed, which takes the classification-only branch (pkg/repository/item.go:285-298). So extracted_content stays empty and extracted_at stays NULL for the life of the row.
  • ItemExists makes later feed updates skip the GUID, so the live path never revisits it either.

Reproduction

  1. Run with extraction.enabled: false and a working LLM endpoint, and add a feed.
  2. Break the LLM endpoint (stop it, or point llm.endpoint at a closed port) and let an update cycle run: [WARN] failed to classify batch appears and the new rows land with classified_at NULL.
  3. Restart newscope, with the endpoint working again.

Observed: SELECT count(*) FROM items WHERE classified_at IS NULL does not move, neither startup pass picks anything up, and those articles never appear in the UI, which lists classified rows only. Nothing queries them again for as long as the database lives.

Expected: they are classified once the endpoint recovers.

Impact

Articles are stored and then invisible for good after an ordinary transient outage plus a restart. Extraction is off by default when the section is absent from the config (pkg/config/config.go:81), so this is not an exotic setup. The gap became reachable only with #43, which stopped that mode from panicking on the first item; before that, extraction-disabled installs died immediately, so nothing regressed here.

Options

  1. A startup query for unclassified rows that does not require extracted content. Add GetItemsNeedingClassification(ctx, limit) (classified_at IS NULL AND extraction_error = '') to the repository and to the scheduler's ItemManager, and use it instead of the two loops when extraction is disabled, feeding the existing processing worker. It drains as classification sets classified_at, so the endless re-query that the current guard avoids cannot come back. Cost: one query and one interface method.
  2. Store the feed content as the extracted content when extraction is off, which makes both existing queries work untouched. Cheaper, but it changes what extracted_content means, shows feed content in the UI as though it had been extracted, and leaves those rows permanently un-extractable if extraction is enabled later, since extracted_at would be set. I would not do this without you asking for it.
  3. Leave it and document the mode as fire-and-forget: with extraction disabled, an item missed at ingest is not retried. Cheapest, and defensible if that mode is secondary.

I would take option 1. Happy to send the PR with a regression test that fails classification, restarts the scheduler and asserts the item is picked up, once you say which shape you want.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with processExistingItems in pkg/scheduler/scheduler.go, then read the item queries and UpdateItemProcessed in pkg/repository/item.go, along with extractContent and ProcessBatch in pkg/scheduler/feed_processor.go. Add a regression test for extraction-disabled classification failure followed by scheduler restart; done means the previously unclassified item is picked up and classified after recovery.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.