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>
This commit is contained in:
Daan MeijerandClaude Fable 5.1 committed 2026-10-25 10:31:35 +01:00
1 parent 137e931e9e
commit 33405eb8ca
2 files changed
+41 -23

No files matched your search

+33 -17
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 {
await client.xover(point, Math.min(point + options.size - 1, info.high)), const date = await use(async (client) => {
now await client.group(name)
) commands += 2
if (!date) { const slice = medianDate(
commands++ await client.xover(point, Math.min(point + options.size - 1, info.high)),
date = medianDate( now
await client.xover(point, Math.min(point + options.size * 32 - 1, info.high)), )
now if (slice) return slice
) commands++
return medianDate(
await client.xover(point, Math.min(point + options.size * 32 - 1, info.high)),
now
)
})
if (date) samples.push({ article: point, postedAt: date })
} catch (error: any) {
failed.push(`${point}: ${error.message}`)
} }
if (date) samples.push({ article: point, postedAt: date })
} }
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