Store a new row's earliest timestamp as first_seen (closes #112)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Each handler took the latest time per key in a batch and inserted it as a new row's first_seen, so a key updated twice in one batch, seconds apart, was stored first seen at the later time, and the IP lookup could answer with a prefix first_seen after the route's last_seen. The prefix handler now keeps the earliest time of all of the batch's updates for each prefix. The AS, peer and peering handlers keep the earliest and the latest time per key in database.SeenTimes, and GetOrCreateASNBatch, UpdatePeerBatch and RecordPeering store a new row first seen at the earliest and last seen at the latest. A stored row still has only its last_seen set, to the latest. Model: opus-5-5
This commit was merged in pull request #137.
This commit is contained in:
@@ -615,10 +615,12 @@ func TestBatchWriteDuringCheckpoint(t *testing.T) {
|
||||
})
|
||||
|
||||
ts := time.Now().UTC()
|
||||
seen := SeenTimes{First: ts, Last: ts}
|
||||
|
||||
for i := range contentionIterations {
|
||||
asns := map[int]time.Time{
|
||||
i % contendedASNCount: ts,
|
||||
(i % contendedASNCount) + asnSecondBand: ts,
|
||||
asns := map[int]SeenTimes{
|
||||
i % contendedASNCount: seen,
|
||||
(i % contendedASNCount) + asnSecondBand: seen,
|
||||
}
|
||||
|
||||
err := db.GetOrCreateASNBatch(asns)
|
||||
@@ -747,7 +749,8 @@ func TestBatchWriteWaitsForAnalyze(t *testing.T) {
|
||||
|
||||
db, messages := newDatabaseWithLockMessages(t)
|
||||
ctx := context.Background()
|
||||
asns := map[int]time.Time{lockTestASN: time.Now().UTC()}
|
||||
now := time.Now().UTC()
|
||||
asns := map[int]SeenTimes{lockTestASN: {First: now, Last: now}}
|
||||
|
||||
// A stored AS gives ANALYZE a table to analyze and statistics to write.
|
||||
err := db.GetOrCreateASNBatch(asns)
|
||||
@@ -1663,6 +1666,48 @@ func TestUpdatePrefixesBatchKeepsFirstSeen(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdatePeerBatchStoresTheEarliestAndLatestTimesOfANewPeer writes a new
|
||||
// peer whose updates span 5 seconds. Its row is first seen at the earliest time
|
||||
// and last seen at the latest.
|
||||
func TestUpdatePeerBatchStoresTheEarliestAndLatestTimesOfANewPeer(
|
||||
t *testing.T,
|
||||
) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := New(&config.Config{StateDir: t.TempDir()}, logger.New())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database: %v", err)
|
||||
}
|
||||
defer func() { _ = db.Close() }()
|
||||
|
||||
first := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
last := first.Add(5 * time.Second)
|
||||
|
||||
err = db.UpdatePeerBatch(map[string]PeerUpdate{
|
||||
testPeerIP: {
|
||||
PeerIP: testPeerIP, PeerASN: 64500, MessageType: "UPDATE",
|
||||
Seen: SeenTimes{First: first, Last: last},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdatePeerBatch: %v", err)
|
||||
}
|
||||
|
||||
var firstSeen, lastSeen time.Time
|
||||
|
||||
err = db.db.QueryRowContext(t.Context(),
|
||||
"SELECT first_seen, last_seen FROM bgp_peers WHERE peer_ip = ?",
|
||||
testPeerIP).Scan(&firstSeen, &lastSeen)
|
||||
if err != nil {
|
||||
t.Fatalf("reading peer %s: %v", testPeerIP, err)
|
||||
}
|
||||
|
||||
if !firstSeen.Equal(first) || !lastSeen.Equal(last) {
|
||||
t.Errorf("peer %s: first seen %s, last seen %s; want %s, %s",
|
||||
testPeerIP, firstSeen, lastSeen, first, last)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpsertLiveRouteBatchWritesInKeyOrder writes new routes of two peers out
|
||||
// of key order, the last one a second announcement of the first, and checks
|
||||
// that the rows were inserted in key order, peer then prefix (each new row's id
|
||||
|
||||
Reference in New Issue
Block a user