From ae70567e636178ee2d28eba1da3ed6b02fbdb32a Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 4 Nov 2026 19:34:09 +0100 Subject: [PATCH] refactor: IndexNow request batches and answer outcome as functions No behaviour change: NotifyIndexNow splits through indexNowBatches and SendIndexNow maps the answer through indexNowOutcome, so both can be unit tested without a queue or the network. --- classes/indexnow.go | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/classes/indexnow.go b/classes/indexnow.go index eb58955..6a76563 100644 --- a/classes/indexnow.go +++ b/classes/indexnow.go @@ -142,15 +142,24 @@ func NotifyIndexNow(ctx context.Context, app *backpack.App, db *gorm.DB, res Reb if err != nil { return err } - for start := 0; start < len(urls); start += MaxIndexNowURLs { - end := min(start+MaxIndexNowURLs, len(urls)) - if err := m.Enqueue(ctx, db, IndexNowArgs{URLs: urls[start:end]}, conga.EnqueueOpts{}); err != nil { + for _, batch := range indexNowBatches(urls) { + if err := m.Enqueue(ctx, db, IndexNowArgs{URLs: batch}, conga.EnqueueOpts{}); err != nil { return err } } return nil } +// indexNowBatches splits urls into requests of at most MaxIndexNowURLs, in +// order. +func indexNowBatches(urls []string) [][]string { + var out [][]string + for start := 0; start < len(urls); start += MaxIndexNowURLs { + out = append(out, urls[start:min(start+MaxIndexNowURLs, len(urls))]) + } + return out +} + // indexNowBody is the JSON of an IndexNow request. keyLocation is not sent: // the key file is served at the site root, which covers every address of // the host (P33-14). @@ -214,11 +223,11 @@ func SendIndexNow(ctx context.Context, app *backpack.App, args IndexNowArgs) err time.Now().UTC(), len(urls), status).Error; err != nil { return err } - switch { - case status == http.StatusOK || status == http.StatusAccepted: + switch indexNowOutcome(status) { + case indexNowSent: pluginLog.Info("golem15.sitemap: indexnow notified", "urls", len(urls), "status", status) return nil - case status == http.StatusTooManyRequests || status >= 500: + case indexNowRetry: pluginLog.Warn("golem15.sitemap: indexnow will retry", "urls", len(urls), "status", status) return errIndexNowRetry{status: status} default: @@ -227,6 +236,26 @@ func SendIndexNow(ctx context.Context, app *backpack.App, args IndexNowArgs) err } } +// The outcomes of an IndexNow answer. +const ( + indexNowSent = iota + indexNowRetry + indexNowRefused +) + +// indexNowOutcome maps an IndexNow answer's status: 200 and 202 are sent, +// 429 and 5xx are retried, everything else (400, 403, 422) is refused and +// not retried. +func indexNowOutcome(status int) int { + switch { + case status == http.StatusOK || status == http.StatusAccepted: + return indexNowSent + case status == http.StatusTooManyRequests || status >= 500: + return indexNowRetry + } + return indexNowRefused +} + // EnsureKey gives settings row 1 an IndexNow key on save: while enabled, // a new key is generated when none is stored or rotate is set (the // indexnow_new_key checkbox), so the old key file answers 404 at once. It