2 Commits
Author SHA1 Message Date
Daan Meijer c825dc7a8f Merge branch 'rebuild' into voyager-3
Test and deploy / test (push) Successful in 49s
Test and deploy / edge (push) Successful in 2m0s
Test and deploy / deploy (push) Skipped
2026-10-25 10:32:39 +01:00
Daan MeijerandClaude Fable 5.1 33405eb8ca ranges:dates: a point on a connection of its own; --missing still dates every group
Test and deploy / test (push) Successful in 54s
Test and deploy / edge (push) Successful in 2m4s
Test and deploy / deploy (push) Successful in 1m53s
Sixteen groups never got through on one connection (a reply timed out
after 2 minutes); now a timed-out point is skipped and logged, the
group goes on, and stops after 3 failures. estimate() ignored --missing's
filter: it dated 0 groups on the second run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-25 10:31:35 +01:00
2 changed files with 34 additions and 16 deletions

No files matched your search

+26 -10
View File
@@ -90,33 +90,49 @@ export function sampleCount(
* server's span. * server's span.
*/ */
export async function sampleGroup( export async function sampleGroup(
client: XoverClient, use: <T>(work: (client: XoverClient) => Promise<T>) => Promise<T>,
name: string, name: string,
options: { every: number; size: number }, options: { every: number; size: number; maxFailures?: number },
now = new Date() now = new Date()
): Promise<{ samples: DateSample[]; low: number; high: number; commands: number }> { ): Promise<{
const info = await client.group(name) samples: DateSample[]
low: number
high: number
commands: number
failed: string[]
}> {
const info = await use((client) => client.group(name))
const samples: DateSample[] = [] const samples: DateSample[] = []
const failed: string[] = []
let commands = 1 let commands = 1
// Each point on a connection of its own (GROUP again): a reply that times out (Eweka stalls some requests for
// minutes; 16 groups never got through on one connection) costs that point, not the group.
for (const point of samplePoints( for (const point of samplePoints(
info.low, info.low,
info.high, info.high,
sampleCount(info.low, info.high, options.every), sampleCount(info.low, info.high, options.every),
options.size options.size
)) { )) {
commands++ if (failed.length >= (options.maxFailures ?? 3)) break
let date = medianDate( try {
const date = await use(async (client) => {
await client.group(name)
commands += 2
const slice = medianDate(
await client.xover(point, Math.min(point + options.size - 1, info.high)), await client.xover(point, Math.min(point + options.size - 1, info.high)),
now now
) )
if (!date) { if (slice) return slice
commands++ commands++
date = medianDate( return medianDate(
await client.xover(point, Math.min(point + options.size * 32 - 1, info.high)), await client.xover(point, Math.min(point + options.size * 32 - 1, info.high)),
now now
) )
} })
if (date) samples.push({ article: point, postedAt: date }) if (date) samples.push({ article: point, postedAt: date })
} catch (error: any) {
failed.push(`${point}: ${error.message}`)
} }
return { samples: monotone(samples), low: info.low, high: info.high, commands } }
return { samples: monotone(samples), low: info.low, high: info.high, commands, failed }
} }
+8 -6
View File
@@ -48,10 +48,10 @@ export default class RangesDates extends BaseCommand {
} }
} }
private groups() { private groups(missing = this.missing) {
const q = db.from('groups').where('active', true).select('id', 'name').orderBy('priority') const q = db.from('groups').where('active', true).select('id', 'name').orderBy('priority')
if (this.group) q.where('name', 'like', this.group) if (this.group) q.where('name', 'like', this.group)
if (this.missing) if (missing)
q.whereNotExists((sub) => q.whereNotExists((sub) =>
sub.from('group_dates').whereRaw('group_dates.group_id = groups.id') sub.from('group_dates').whereRaw('group_dates.group_id = groups.id')
) )
@@ -85,9 +85,11 @@ export default class RangesDates extends BaseCommand {
const worker = async () => { const worker = async () => {
for (let g = queue.shift(); g; g = queue.shift()) { for (let g = queue.shift(); g; g = queue.shift()) {
try { try {
const result = await NntpPool.use((client) => const result = await sampleGroup((work) => NntpPool.use(work), g.name, {
sampleGroup(client, g.name, { every, size: 32 }) every,
) size: 32,
})
for (const f of result.failed) this.logger.warning(`${g.name} at ${f}`)
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
await trx.from('group_dates').where('group_id', g.id).delete() await trx.from('group_dates').where('group_id', g.id).delete()
if (result.samples.length > 0) if (result.samples.length > 0)
@@ -129,7 +131,7 @@ export default class RangesDates extends BaseCommand {
/** Every range of the groups sampled gets est_date from the group's curve (1000 a statement). */ /** Every range of the groups sampled gets est_date from the group's curve (1000 a statement). */
private async estimate() { private async estimate() {
const groups = await this.groups().whereExists((sub) => const groups = await this.groups(false).whereExists((sub) =>
sub.from('group_dates').whereRaw('group_dates.group_id = groups.id') sub.from('group_dates').whereRaw('group_dates.group_id = groups.id')
) )
let updated = 0 let updated = 0