Commit Graph
51 Commits
Author SHA1 Message Date
Daan MeijerandClaude Opus 5.5 bef68075be Content probe: don't download the first segment again for whole files
Test and deploy / test (push) Successful in 1m34s
Test and deploy / deploy (push) Successful in 1m40s
Par2 sets, NZBs and SFVs were sniffed from their first segment and then
read whole, downloading that segment a second time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 23:37:26 +02:00
Daan MeijerandClaude Opus 5.5 23bbdcc5ee Seed batches take file ids from the insert; merge split uploads hourly
Test and deploy / test (push) Successful in 1m29s
Test and deploy / deploy (push) Successful in 1m58s
SeedService looked up every file of a batch across all files partitions
after inserting it (15% of Postgres' active time); the merge job read
every release on each GC sweep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 23:09:04 +02:00
Daan MeijerandClaude Opus 5.5 b2cc75fdd0 Keep binaries until inspected; seeding writes binaries only for inspection
Test and deploy / test (push) Successful in 1m24s
Test and deploy / deploy (push) Successful in 6m43s
Par2 naming needs the fingerprint inspection stores on a binary, and the
GC deleted seeded binaries (6 h unfingerprinted, 48 h stale) long before
the inspection queue reached them. binaries.awaiting_inspection protects
them for up to 7 days; the GC deletes use partial indexes so they don't
read past waiting rows. Seeding no longer creates binaries for files that
aren't inspected (~85% of a.b.ath), which only churned the table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:03:43 +02:00
Daan MeijerandClaude Opus 5.5 cc861ce8ae Seed leases alternate between priorities; article relay can be off
Test and deploy / test (push) Successful in 59s
Test and deploy / deploy (push) Successful in 1m33s
Half the live leases go to higher-priority ranges, the rest to the others,
so a slow recent gap runs beside older history. With no article
connections the article relay answers 503 instead of waiting forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 16:19:37 +02:00
Daan MeijerandClaude Opus 5.5 3cc69a617f Seed relay: compressed overviews by default again
Test and deploy / test (push) Successful in 1m3s
Test and deploy / deploy (push) Successful in 1m51s
Measured with 20 relay connections, plain and gzip both gave ~1.5 chunks/s
for 2026 a.b.ath articles; plain used many times the CPU and traffic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 16:14:47 +02:00
Daan MeijerandClaude Opus 5.5 3e6b5bed56 Seed relay fetches plain overviews by default
Test and deploy / test (push) Successful in 1m6s
Test and deploy / deploy (push) Successful in 1m43s
For 2026 a.b.ath articles Eweka's gzip replies have a long tail (p90 12.4 s
per 20k vs 3.9 s plain; 6.1k vs 10.5k headers/s). SEED_NNTP_COMPRESS turns
compression back on; plain replies are deflated for nodes asynchronously.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 15:49:21 +02:00
Daan MeijerandClaude Opus 5.5 7893876435 seed:node splits a batch the seed API finds too large
Test and deploy / test (push) Successful in 1m18s
Test and deploy / deploy (push) Successful in 1m54s
Dense recent a.b.ath ranges produced batches over the 64 MB body limit
(413). The node now posts such a batch in halves; only the second moves
the range's resume point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 15:21:41 +02:00
Daan MeijerandClaude Opus 5.5 f0ff8e200f file_keys: fill in the background, use it once complete
Test and deploy / test (push) Successful in 1m37s
Test and deploy / deploy (push) Successful in 2m23s
Copying all existing keys in the migration read the files rows from the
HDD for over half an hour and held up the deploy. gc:scheduler now copies
them in slices (FILE_KEYS_FILL_SECONDS per sweep) and FileStore checks
every partition until the fill is complete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:54:12 +02:00
Daan MeijerandClaude Opus 5.5 693e6c3244 Seed plans fill every uncovered gap, with priorities; cheaper ingest and file storing
Test and deploy / test (push) Successful in 1m27s
Test and deploy / deploy (push) Failing after 33m7s
- seed:plan plans every part of a group's history no range covers (it
  started below the lowest known range and left a.b.ath's 13 most recent
  months out); seed_ranges.priority orders leases (seed:plan --priority).
- binaries.part_count, kept by the parts insert in the same statement:
  claimCompleted compares it instead of counting parts every batch.
- file_keys: 64-bit hashes of all file keys; FileStore.insert asks it
  before probing every files partition.
- worker:file stores 100 completed binaries per job with set-based queries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:18:01 +02:00
Daan MeijerandClaude Opus 5.5 61b683a71f Release bookkeeping migration: take the files locks first
Test and deploy / test (push) Successful in 1m19s
Test and deploy / deploy (push) Successful in 2m6s
The trigger swap deadlocked with ingest on homelab (DROP TRIGGER locks
the partitions one by one); LOCK TABLE up front cannot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:53:52 +02:00
Daan MeijerandClaude Opus 5.5 be2112f4ad Par2 size matching 100x faster; release bookkeeping once per statement
Test and deploy / test (push) Successful in 1m33s
Test and deploy / deploy (push) Failing after 1m29s
matchPar2Releases checked every same-size release_files row's set and
date (18 s per batch of body-derived binaries); now an index on
release_files (release_id, size) drops the repeated sizes first: 0.16 s.

File inserts updated their release in four row triggers; one statement
trigger now updates each release once per INSERT. Completeness is noted
in release_dirty and recomputed by gc:scheduler every 30 minutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:48:59 +02:00
Daan MeijerandClaude Opus 5.5 288a6bab8d seed:node spreads its ranges and inspections over worker processes
Test and deploy / test (push) Successful in 2m51s
Test and deploy / deploy (push) Successful in 2m27s
One process ran everything on one core. SEED_PROCESSES (default: the
number of cores) worker processes now each run seed:node with a share of
the slots; stop signals are passed on and crashed workers restarted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:25:00 +02:00
Daan MeijerandClaude Opus 5.5 4d17c5a8e8 Release completeness check without reading every binary; SEED_RANGES
Test and deploy / test (push) Successful in 2m15s
Test and deploy / deploy (push) Successful in 2m26s
release_complete() matched each par2-listed file through a correlated
subquery that scanned all binaries per listed file, run twice per
inserted release_files row: one insert of a few thousand rows ran for
20+ minutes. It now reads the release's files once by release id, and
release_files refreshes once per INSERT statement.

seed:node's SEED_CONNECTIONS/--connections is now SEED_RANGES/--ranges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 12:10:57 +02:00
Daan MeijerandClaude Opus 5.5 2dbca9b152 Content inspection and archive walks on seeding nodes (relay)
Test and deploy / test (push) Successful in 1m49s
Test and deploy / deploy (push) Successful in 2m15s
Inspection is split into prepare (database), probe (lib/content/probe:
downloads, decoding and parsing, no database) and apply (database).
worker:collection runs all three; seed:node with SEED_INSPECTORS leases
jobs from the same queues through the seed API (BullMQ manual leases),
fetches articles through the seed API and posts the probes back.

A node now needs only SEED_URL and SEED_TOKEN: the settings the app
requires but a node never uses get placeholders for seed:node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 11:17:02 +02:00
Daan MeijerandClaude Opus 5.5 51975fe603 Hidden archive entries no longer name releases
Test and deploy / test (push) Successful in 1m44s
Test and deploy / deploy (push) Successful in 2m6s
One poster adds the same 22 GB hidden '.1917-….mkv' decoy to every 7z
upload; the largest-entry rule named 840 releases after it. Entries with
a hidden path part are skipped; release:rename --hidden renames the
existing ones from their stored listing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:29:05 +02:00
Daan MeijerandClaude Opus 5.5 4d3cb6474c 7z: encrypted headers are known from the encoded header, without bsdtar
Test and deploy / test (push) Successful in 2m6s
Test and deploy / deploy (push) Successful in 2m25s
85% of the 7z archives inspected have encrypted headers (-mhe). Their
encoded header lists the 7zAES coder, so the packed header stream is no
longer downloaded and bsdtar no longer runs for them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:23:32 +02:00
Daan MeijerandClaude Opus 5.5 9ae15ed26f Seed batches: test chunked inserts with a lowered chunk size
Test and deploy / test (push) Successful in 1m57s
Test and deploy / deploy (push) Successful in 4m7s
The 6,000-file test took 29 s and failed CI; 120 files in chunks of 50
cover the same path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:20:17 +02:00
Daan MeijerandClaude Opus 5.5 953bbe9e39 Seed batches: insert files and binaries in chunks of 2,000 rows
Test and deploy / test (push) Failing after 3m16s
Test and deploy / deploy (push) Skipped
A batch of a dense range (over ~5,400 files) exceeded Postgres' 65,535
bind parameters per statement, so its post failed with a 500 until the
range was given up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:01:32 +02:00
Daan MeijerandClaude Opus 5.5 f4ded062df Complete archive listings by walking RAR headers, at low priority
Test and deploy / test (push) Successful in 1m24s
Test and deploy / deploy (push) Successful in 1m58s
Inspection lists only the entries whose headers are in the first segment
of a first volume. A walk hops from header to header, fetching one segment
per entry and volume, in its own queue (one job per collection worker) on
NNTP connections inspection isn't waiting for. content:walk-archives queues
releases inspected earlier (CONCEPT D40).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:32:52 +02:00
Daan MeijerandClaude Opus 5.5 b7ae8267c6 Seed relay: a stalled compressed XOVER is fetched again uncompressed
Test and deploy / test (push) Successful in 1m9s
Test and deploy / deploy (push) Successful in 1m41s
The four ranges kept failing on fresh connections too: in gzip mode Eweka
stops some chunks partway (a.b.ath 49701720000: 19,672 of 20,000
overviews, the last line cut, then silence), while the same chunk without
compression comes in 0.3 s. A failed relay chunk is now retried once
without compression on a connection of its own (dead pooled connections are
covered by the same retry).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 21:16:39 +02:00
Daan MeijerandClaude Opus 5.5 028dd90a6f Seed relay: dead NNTP connections found in 90 s and retried
Test and deploy / test (push) Successful in 1m14s
Test and deploy / deploy (push) Successful in 1m46s
Five ranges "never answered" in 10 minutes, yet their chunk came back in
0.3 s on a fresh connection: the news server drops idle connections
without telling, and the pool's next request on one waited for the
10-minute inactivity timeout. Now: SEED_NNTP_TIMEOUT_MS 90 s (inactivity;
slow ranges keep streaming), one retry on a fresh connection, and pooled
connections idle over 60 s are replaced before use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:51:19 +02:00
Daan MeijerandClaude Opus 5.5 8d86f7d489 Seed fragments: keep only those near the range edges, unstaged
Test and deploy / test (push) Successful in 1m5s
Test and deploy / deploy (push) Successful in 1m48s
About 5% of seeded headers are fragments. They went through staging,
where they waited 30 minutes for parts that were never coming (the
neighbouring range is worked on by another node, often hours later) and
expired: the staging table grew to 22 GB and the NVMe part of the database
reached its 60 GB limit, which stopped leasing and the backfill. Files cut
by a range edge can still be completed by the neighbouring range, so
fragments within 50k articles of an edge go straight into binaries and
parts; the rest, files with articles missing for good, are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:30:24 +02:00
Daan MeijerandClaude Opus 5.5 530a752cca Seed: finished ranges marked done; relay XOVER timeout 10 minutes
Test and deploy / test (push) Successful in 1m37s
Test and deploy / deploy (push) Successful in 2m10s
- A range reported to its end but not finished (its node went away at
  the very end) was leased again with start past end; nodes asked for
  "from 49208000000 to 49207999999", got 400, and the range failed after
  five leases. The orchestrator now marks such ranges done before
  leasing, and runRange reports them done without fetching.
- Some old ranges come so slowly that one XOVER of 20k headers takes over
  two minutes: five ranges failed on "NNTP reply timed out". The relay
  pool's timeout is SEED_NNTP_TIMEOUT_MS (default 10 minutes, the node's
  HTTP timeout).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:21:30 +02:00
Daan MeijerandClaude Opus 5.5 ef09c07d6e files.id migration in one transaction
Test and deploy / test (push) Successful in 1m51s
Test and deploy / deploy (push) Successful in 49s
A failure after the old parent is dropped would leave files half-converted;
in one transaction it rolls back completely. Tested on a fresh Postgres 17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:48:11 +02:00
Daan MeijerandClaude Opus 5.5 9201e1200e files.id to 64 bits
Test and deploy / test (push) Successful in 1m52s
Test and deploy / deploy (push) Skipped
Seeding adds ~14M files a day; 32-bit ids run out at 2.1 billion in about
five months. Postgres can't change a partition key's type, so the
migration detaches the partitions, rewrites each as a plain table (a CHECK
added first spares the attach scan), and attaches them to a new 64-bit
parent with the same indexes, foreign keys and triggers. binaries.file_id
and the id sequence become 64-bit too. Meant to run with the writers
stopped and Postgres on wal_level=minimal (compose: PG_WAL_LEVEL,
PG_MAX_WAL_SENDERS).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 16:46:53 +02:00
Daan MeijerandClaude Opus 5.5 f1773744c0 Partition files by age (id)
Test and deploy / test (push) Successful in 54s
Test and deploy / deploy (push) Successful in 7m42s
New rows went into free space scattered through the 9.6 GB heap on the HDD
(left behind by inspection's updates): one random HDD read per insert, and
seed batches took a median 13 s to store. The existing table becomes the
first partition as it is (a CHECK scan, no rewrite; indexes, its foreign
key and the triggers are taken over by the partitioned parent); new
partitions of 4M ids go in the same tablespace and stay in memory while
they fill.

- Every partition has a unique file_key index; FileStore.insert looks keys
  up in all partitions first (a key can't be unique across partitions).
- FilePartitions.ensure() (gc:scheduler) keeps two partitions ahead.
- Stats count files from the partitions; seed:node waits ~20 minutes for
  the API before giving a range up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:57:51 +02:00
Daan MeijerandClaude Opus 5.5 41859636d3 Seed relay: nodes get their headers through the seed API
Test and deploy / test (push) Successful in 1m28s
Test and deploy / deploy (push) Successful in 2m5s
The seed API keeps a pool of NNTP connections (SEED_NNTP_CONNECTIONS) and
serves GET /api/v1/seed/ranges/:id/chunk?from=&to= for leased ranges:
the gzip stream the news server sent, passed on unopened. seed:node fetches
that way by default (SEED_FETCH=relay), so a node needs only a token and
SEED_CONNECTIONS; SEED_FETCH=direct keeps its own NNTP connections. All
fetching then comes from the indexer's IP (the account allows two).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:24:39 +02:00
Daan MeijerandClaude Opus 5.5 3d5cfb2527 Seeding nodes lease first, connect for the lease, close when idle
Test and deploy / test (push) Successful in 1m36s
Test and deploy / deploy (push) Successful in 1m58s
A failed NNTP login gives the range back uncounted (no attempt used), and a
node with nothing to lease holds no connection. Replaces log-in-before-lease,
which kept idle connections and logged in again every minute while idle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 15:18:38 +02:00
Daan MeijerandClaude Opus 5.5 70036847cf Seeding nodes log in before leasing; TLS through tunnels
Test and deploy / test (push) Successful in 1m29s
Test and deploy / deploy (push) Successful in 1m59s
- seed:node logs in to NNTP before taking a range and keeps the connection
  across ranges (replaced after 2 minutes idle or a failed range). A node
  that can't reach its news server takes no range: two ranges were set
  aside as failed while an office node's tunnel was being set up
  (ECONNREFUSED 127.0.0.1:1563, five leases each). After an IP-address
  limit refusal it waits 5 minutes.
- NNTP_TLS_SERVERNAME: the hostname the TLS certificate is checked
  against when NNTP_HOST is a tunnel ("Hostname/IP does not match
  certificate's altnames: Host: localhost").
- seed-node/README: the IP-address limit and tunnelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:14:15 +02:00
Daan MeijerandClaude Opus 5.5 04f945055f XoverClient times replies only, not idle time between commands
Test and deploy / test (push) Successful in 1m12s
Test and deploy / deploy (push) Successful in 1m37s
A seeding node that waited over two minutes for the seed API (restarting)
had its idle NNTP connection closed by its own timeout, and gave its range
back. Now the timeout runs only while a reply is awaited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:40:15 +02:00
Daan MeijerandClaude Opus 5.5 00f6187b8f Seed leases respect the backfill's disk limits
Test and deploy / test (push) Successful in 1m8s
Test and deploy / deploy (push) Successful in 1m37s
No new range while the database's NVMe part or the hdd tablespace is over
BACKFILL_MAX_NVME_GB / BACKFILL_MAX_HDD_GB; nodes ask again a minute later.
seed:status says when leasing is paused. The NVMe part was at 47 GB of 50
with a plan for all of a.b.ath queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:30:46 +02:00
Daan MeijerandClaude Opus 5.5 f9e9295d3e seed-node: step-by-step guide for running a node on a remote server
Test and deploy / test (push) Successful in 1m12s
Test and deploy / deploy (push) Failing after 43s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 13:11:17 +02:00
Daan MeijerandClaude Opus 5.5 2b14b0dca5 seed:node retries 404 from the proxy (seed API restarting)
Test and deploy / test (push) Successful in 1m0s
Test and deploy / deploy (push) Successful in 1m42s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:17:54 +02:00
Daan MeijerandClaude Opus 5.5 e2acfae98d seed:node posts batches in order beside fetching
Test and deploy / test (push) Successful in 57s
Test and deploy / deploy (push) Successful in 1m44s
At most two posts waiting; a failed post stops the chain, so nothing is
stored past a missing batch. The first remote node did 7k headers/s while
waiting for every post.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:09:16 +02:00
Daan MeijerandClaude Opus 5.5 8c36744088 Seed orchestrator: remote nodes lease ranges and post results over HTTPS
Test and deploy / test (push) Successful in 1m4s
Test and deploy / deploy (push) Successful in 1m37s
- seed_nodes (hashed bearer tokens) and seed_ranges (planned ranges with
  lease, resume point and counts); migration …140.
- SeedOrchestrator: lease (oldest plan first, FOR UPDATE SKIP LOCKED; a
  lease without a batch for 15 min passes to the next node and resumes at
  next_id), batch (stores files and fragments, moves next_id), finish,
  release, plan (from below live indexing / the backfill / earlier plans
  down to N days back; takes over the group's backfill), status.
- Seed API under /api/v1/seed (lease, ranges/:id/batches|done|release,
  status) behind a node-token middleware; JSON bodies up to 64 MB after
  gzip.
- seed:node runs a node: no database or Redis, outgoing HTTPS only, N
  connections, gzip batches, retries for network errors and 5xx, drops a
  range on 409, gives its ranges back on SIGTERM. seed-node/ has a compose
  file that builds the image from this repository.
- seed:token, seed:plan, seed:status for the operator. Header parsing moved
  to lib/ingest and the range loop to lib/seed/runner, shared by seed:range
  and seed:node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:02:01 +02:00
Daan MeijerandClaude Opus 5.5 97bdf8e982 Seeding node: gzip overviews, batched SFV lookups, fragments beside fetching
Test and deploy / test (push) Successful in 1m0s
Test and deploy / deploy (push) Successful in 1m43s
- lib/nntp/xover_client: a minimal one-connection client for seed:range
  with XFEATURE COMPRESS GZIP (a zlib stream after "224 … [COMPRESS=GZIP]",
  then a "." line; 420 for an empty range). About 6x less header traffic.
  Overviews come out with the field names nntp-js uses.
- SfvService.claimAll: one sfv_entries lookup per stored batch.
- Fragments go through the regular ingest path beside fetching (at most
  two batches in flight); a failure fails the run at the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:11:41 +02:00
Daan MeijerandClaude Opus 5.5 68eb9a44fd Edge aggregation: seed:range indexes history one row per file
Test and deploy / test (push) Successful in 1m36s
Test and deploy / deploy (push) Successful in 3m37s
A seeding node fetches a range over one NNTP connection, parses headers
with the live rules, groups them into files in memory (lib/aggregate) and
stores per complete file its release, the file and a completed binary
(SeedService), instead of one parts row per article. What doesn't complete
within the window goes through the regular ingest path as fragments.
`--dry-run` only fetches and aggregates, to measure a node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 10:41:18 +02:00
Daan MeijerandClaude Opus 5.5 7ca338d4f3 Extension filter via a releases.extensions column
Test and deploy / test (push) Successful in 1m28s
Test and deploy / deploy (push) Successful in 58m9s
The first version matched extensions in search_text, which was wrong and
slow: file names enter search_text as stems without extension (so plainly
posted .mkv files were missed), and matching the TOASTed text took 3-4 s
for common types.

- Migration …130: releases.extensions text[] (two to four characters with
  a letter; .7z.001 and .r00 count as 7z and rar), kept current by triggers
  on files (name, release) and on releases (archive listing), filled with
  one sequential pass over files, with a GIN index.
- Search: ext matches with `extensions && ?`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:54:38 +02:00
Daan MeijerandClaude Opus 5.5 e1392608d8 Search filters for size (MB) and file extensions
Test and deploy / test (push) Successful in 1m27s
Test and deploy / deploy (push) Successful in 2m9s
- API: min_size and max_size in MB, ext as a comma-separated list. An
  extension matches as a whole word in search_text (file and entry names,
  dots as spaces), so "iso" finds .iso files but not "ISOLATION", which a
  plain search word matched as a substring. The trigram index serves it.
- Web UI: a size range and an extension field in the filter row, kept in
  the URL like the other filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:43:55 +02:00
Daan MeijerandClaude Opus 5.5 95f1d427df Inspect fewer files: skip later volumes under random stems
Test and deploy / test (push) Successful in 1m25s
Test and deploy / deploy (push) Successful in 2m58s
Of 5,000 waiting inspection jobs, 3,212 were queued only because their
stem looked random, and 2,044 of those were later RAR volumes
(xyz.part47.rar, .r00) or par2 recovery volumes (.vol03+04.par2). Their
first segment adds nothing: the set is listed from its first volume and
named from its index .par2, as for readable names already. Each one cost a
~700 KB download; inspection pulled ~85 MB/s and fell behind the backfill.

- isCandidate() rejects later volumes (isLaterVolume) before the random-
  name rule.
- worker:file only queues files that are candidates, so the ~30% that
  inspection skips anyway no longer take queue slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 09:32:40 +02:00
Daan MeijerandClaude Opus 5.5 2178847513 Stop refilling parts of binaries that are already a file
Test and deploy / test (push) Successful in 52s
Test and deploy / deploy (push) Successful in 1m23s
Crossposts, reposts and backfill ranges keep bringing articles of files
that worker:file already created. Ingest inserted their header parts again,
and GC's copied-parts pass (which searched all binaries for parts on every
batch) removed only 150k-450k per sweep: parts grew to 68M rows, 56M of
them belonging to finished files, and the 23 GB table paused the backfill.

- Ingest skips header parts (no per-part CRC) for binaries with a file_id.
- GC drops copied parts with a cursor over binaries, 2000 per statement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:53:11 +02:00
Daan MeijerandClaude Opus 5.5 d3e774a489 Add Gitea Actions: test every push, deploy master to homelab
Test and deploy / test (push) Successful in 4m20s
Test and deploy / deploy (push) Successful in 1m55s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:04:48 +02:00
Daan MeijerandClaude Opus 5.5 be8a3d4580 Spotnet import, header backfill, staging and counter grouping, faster search
Spotnet (CONCEPT D29):
- Index free.pt fully and import spots with their descriptions, categories
  and NZBs; a spot renames its release, since Spotnet names are more
  trustworthy than names derived from subjects.
- Find passwords in spot descriptions too, and tell "needs a password we
  have" from "needs a password we don't have".

Content inspection:
- List the contents of 7z (.7z.001) archives, SFV sets, nicovideo uploads
  and other archive-like files; mark yEncBin .bin containers as encrypted.
- Take real names from yEnc headers for randomly named files, and merge
  uploads that were split across posts.

Storage (D31, D32):
- Store segment lists zstd-compressed behind the FileStore accessor.
- group:backfill indexes older headers at low priority and pauses when
  the NVMe or HDD part of the database gets too large.

Grouping (D35, D36):
- Lone header segments wait in staged_segments until a second segment of
  their file arrives, which drops the per-article junk binaries.
- Counter grouping joins random-subject uploads by poster, [i/M] counter,
  part count and day.

Search and maintenance (D33, D34):
- A trigram-indexed search_text column, smaller indexes, estimated stats.
- Passes that clean up nonsense names; weekly index maintenance; GC budgets
  per kind.
- Web UI: grey out stale results while a search runs and cancel superseded
  requests. Count only up to 1,000 matches for the page, which made filter
  clicks take seconds (Newznab keeps the 10,000 cap).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 19:59:23 +02:00
Daan Meijer bcb9be5da9 implemented a lot of features 2026-09-29 00:32:01 +02:00
Daan Meijer 62f1767df3 omgebouwd naar adonisjs, werkt nog niet echt goed 2026-06-03 23:41:09 +02:00
Daan Meijer dfd3336447 download seems to work, par2 verification worked at least once 2026-06-01 00:36:34 +02:00
Daan Meijer ba6cb101a6 crc checks work 2026-05-31 22:48:00 +02:00
Daan Meijer fc947953e4 application restructuring, added crc check when decoding yenc files 2026-05-31 22:07:14 +02:00
Daan Meijer efbc1237d6 downloader can handle concurrent connections 2026-05-31 21:23:18 +02:00
Daan Meijer b5647521fd yenc functions decode 1 part files correctly 2026-05-31 21:12:05 +02:00