Bound each shutdown stage by the stop deadline (closes #100)
check / check (push) Canceled after 0s

Shutdown passes its context to every stage: the streamer, the four
handlers, the WHOIS fetcher, the database maintainer and the HTTP server
each stop waiting when it ends. A handler then skips its last write and
logs how many updates it did not write, and the HTTP server closes the
connections still open. The database is checkpointed and closed last,
and left open once the deadline has passed, since a write may still
hold it. The stop hook gives Shutdown 8 seconds and fx 9, inside the 10
seconds upaas and docker stop allow, so a stop signal exits 0.

Model: opus-5-5
This commit was merged in pull request #130.
This commit is contained in:
2026-10-20 02:15:10 +02:00
parent 237f73fc49
commit 09d3073fa5
20 changed files with 1097 additions and 130 deletions
@@ -1,11 +1,13 @@
package routewatch
import (
"bytes"
"context"
"errors"
"fmt"
"net/netip"
"slices"
"strings"
"sync"
"testing"
"time"
@@ -766,6 +768,53 @@ func TestPrefixHandlerKeepsAtMostABatchOfUpdatesWhileADeleteKeepsFailing(
}
}
// TestPrefixHandlerStopLogsAFailedDeleteWithoutTheDeadline stops a prefix
// handler, with no deadline, after a peer's session went down and the peer
// announced a prefix, into a database whose every delete of a peer's routes
// fails. Stop must log the peer down and the announcement as not written, and
// must not say that the stop deadline passed.
func TestPrefixHandlerStopLogsAFailedDeleteWithoutTheDeadline(t *testing.T) {
t.Parallel()
var logs bytes.Buffer
h := NewPrefixHandler(peerDeleteAlwaysFailsStore{Database: newTestDatabase(t)},
newErrorLogger(&logs))
h.HandleMessage(sessionDown())
h.HandleMessage(peerAnnouncement(testPeerIP, testV4Prefix))
h.Stop(t.Context())
logged := lastLogged(t, &logs)
if strings.Contains(logged.Msg, "deadline") {
t.Errorf("Stop logged %q although its deadline had not passed",
logged.Msg)
}
if logged.Updates != 1 || logged.PeersDown != 1 {
t.Errorf("Stop logged %d updates and %d peer downs not written, "+
"want 1 and 1", logged.Updates, logged.PeersDown)
}
}
// TestPrefixHandlerStopLogsNothingAfterADeleteThatSucceeds stops a prefix
// handler, with no deadline, after a peer's session went down, into a database
// whose delete of the peer's routes succeeds. Stop must log nothing: a peer
// whose routes are deleted no longer counts as a peer down left undone.
func TestPrefixHandlerStopLogsNothingAfterADeleteThatSucceeds(t *testing.T) {
t.Parallel()
var logs bytes.Buffer
h := NewPrefixHandler(newTestDatabase(t), newErrorLogger(&logs))
h.HandleMessage(sessionDown())
h.Stop(t.Context())
if logs.Len() != 0 {
t.Errorf("Stop logged %q after the routes of the peer whose session "+
"went down were deleted", logs.String())
}
}
// newTestDatabase opens a database in a new temporary directory. It is closed
// when the test ends.
func newTestDatabase(t *testing.T) *database.Database {