feat(cluster): feed the locator store from PSK Reporter, filtered at the broker
The store shipped without its main source. Locators came only from this station's own WSJT-X decodes, which is what the whole MQTT discussion was about. PSK Reporter now feeds it through a new OnGrid callback, fired before any geographic filtering: what the store wants is "which square is this callsign in", and that is true whoever happened to hear the report. The subscription filters on the RECEIVER's square, a level the v2 topic exposes. Measured on the live feed: the four opening bands unfiltered are 83 messages a second, of which roughly one in a hundred survived the NearKm test that already existed here — the rest was received, TLS-decrypted, JSON-parsed and discarded. One ring of squares is 0.2 to 1.2 a second. By square rather than by DXCC, which was the obvious alternative: one country measured 1.2 messages a second (OH) against 72.5 (K) on a single band, because a DXCC can be a continent. By square the same measurement is 0.2 to 1.2, so the load follows distance — what the feed is actually about — and is the same for every operator. Grid chasing subscribes with the "+" band wildcard, so one subscription per square covers every band instead of one per band per square. The store gains a source column (decode | mqtt), migrated in place on an existing file.
This commit is contained in:
+11
-11
@@ -22,7 +22,7 @@ func TestDecodeGridRotationKeepsThePreviousGeneration(t *testing.T) {
|
||||
|
||||
// Fill exactly one generation.
|
||||
for i := 0; i < decodeGridsCap; i++ {
|
||||
a.rememberDecodeGrid(fmt.Sprintf("CALL%06d", i), "JN36")
|
||||
a.rememberDecodeGrid(fmt.Sprintf("CALL%06d", i), "JN36", gridcache.SourceDecode)
|
||||
}
|
||||
if got := a.lookupDecodeGrid("CALL000000"); got != "JN36" {
|
||||
t.Fatalf("first entry lost before any rotation: %q", got)
|
||||
@@ -32,7 +32,7 @@ func TestDecodeGridRotationKeepsThePreviousGeneration(t *testing.T) {
|
||||
}
|
||||
|
||||
// One more entry rotates.
|
||||
a.rememberDecodeGrid("NEWCALL", "IO91")
|
||||
a.rememberDecodeGrid("NEWCALL", "IO91", gridcache.SourceDecode)
|
||||
if a.decodeGridsOld == nil {
|
||||
t.Fatal("did not rotate at the cap")
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func TestDecodeGridRotationKeepsThePreviousGeneration(t *testing.T) {
|
||||
|
||||
// A second rotation is what finally retires the oldest half.
|
||||
for i := 0; i < decodeGridsCap; i++ {
|
||||
a.rememberDecodeGrid(fmt.Sprintf("SECOND%06d", i), "KP20")
|
||||
a.rememberDecodeGrid(fmt.Sprintf("SECOND%06d", i), "KP20", gridcache.SourceDecode)
|
||||
}
|
||||
if got := a.lookupDecodeGrid("CALL000000"); got != "" {
|
||||
t.Errorf("the cache is unbounded: %q survived two rotations", got)
|
||||
@@ -63,12 +63,12 @@ func TestDecodeGridRotationKeepsThePreviousGeneration(t *testing.T) {
|
||||
// "f4bpo" would miss a grid learnt as "F4BPO".
|
||||
func TestDecodeGridCaseAndBlanks(t *testing.T) {
|
||||
a := &App{}
|
||||
a.rememberDecodeGrid(" f4bpo ", "JN36")
|
||||
a.rememberDecodeGrid(" f4bpo ", "JN36", gridcache.SourceDecode)
|
||||
if got := a.lookupDecodeGrid("F4BPO"); got != "JN36" {
|
||||
t.Errorf("lookup of the upper-case form failed: %q", got)
|
||||
}
|
||||
a.rememberDecodeGrid("", "JN36")
|
||||
a.rememberDecodeGrid("K1ABC", "")
|
||||
a.rememberDecodeGrid("", "JN36", gridcache.SourceDecode)
|
||||
a.rememberDecodeGrid("K1ABC", "", gridcache.SourceDecode)
|
||||
if got := a.lookupDecodeGrid("K1ABC"); got != "" {
|
||||
t.Errorf("stored an empty grid: %q", got)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func TestDecodeGridConcurrentAccess(t *testing.T) {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 20000; i++ {
|
||||
a.rememberDecodeGrid(fmt.Sprintf("W%05d", i), "FN31")
|
||||
a.rememberDecodeGrid(fmt.Sprintf("W%05d", i), "FN31", gridcache.SourceDecode)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
@@ -112,7 +112,7 @@ func TestOnlyChangesAreQueued(t *testing.T) {
|
||||
defer st.Close()
|
||||
a := &App{gridStore: st}
|
||||
|
||||
a.rememberDecodeGrid("F4BPO", "JN36")
|
||||
a.rememberDecodeGrid("F4BPO", "JN36", gridcache.SourceDecode)
|
||||
if n := st.Pending(); n != 1 {
|
||||
t.Fatalf("a new locator queued %d writes, want 1", n)
|
||||
}
|
||||
@@ -122,14 +122,14 @@ func TestOnlyChangesAreQueued(t *testing.T) {
|
||||
|
||||
// The same report, a hundred times over, is not news.
|
||||
for i := 0; i < 100; i++ {
|
||||
a.rememberDecodeGrid("F4BPO", "JN36")
|
||||
a.rememberDecodeGrid("F4BPO", "JN36", gridcache.SourceDecode)
|
||||
}
|
||||
if n := st.Pending(); n != 0 {
|
||||
t.Errorf("unchanged reports queued %d writes — the batch would carry the whole feed", n)
|
||||
}
|
||||
|
||||
// A station that moved is.
|
||||
a.rememberDecodeGrid("F4BPO", "KP30")
|
||||
a.rememberDecodeGrid("F4BPO", "KP30", gridcache.SourceDecode)
|
||||
if n := st.Pending(); n != 1 {
|
||||
t.Errorf("a changed locator queued %d writes, want 1", n)
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func TestNoRotationWhilePersisting(t *testing.T) {
|
||||
a := &App{gridStore: st}
|
||||
|
||||
for i := 0; i < decodeGridsCap+10; i++ {
|
||||
a.rememberDecodeGrid(fmt.Sprintf("CALL%06d", i), "JN36")
|
||||
a.rememberDecodeGrid(fmt.Sprintf("CALL%06d", i), "JN36", gridcache.SourceDecode)
|
||||
}
|
||||
if a.decodeGridsOld != nil {
|
||||
t.Error("rotated while a store was attached — locators the database holds would go missing")
|
||||
|
||||
Reference in New Issue
Block a user