This commit is contained in:
mr
2026-03-12 15:57:41 +01:00
parent 80117ee36f
commit edcfecd24b
8 changed files with 160 additions and 49 deletions

View File

@@ -27,12 +27,6 @@ import (
"github.com/libp2p/go-libp2p/p2p/security/noise"
)
// activeSearch tracks an in-flight distributed peer search for one user.
type activeSearch struct {
queryID string
cancel context.CancelFunc
}
type Node struct {
*common.LongLivedStreamRecordedService[interface{}] // change type of stream
PS *pubsubs.PubSub
@@ -43,9 +37,8 @@ type Node struct {
isIndexer bool
peerRecord *indexer.PeerRecord
// activeSearches: one streaming search per user; new search cancels previous.
activeSearchesMu sync.Mutex
activeSearches map[string]*activeSearch
// peerSearches: one active peer search per user; new search cancels previous.
peerSearches *common.SearchTracker
Mu sync.RWMutex
}
@@ -85,7 +78,7 @@ func InitNode(isNode bool, isIndexer bool) (*Node, error) {
PeerID: h.ID(),
isIndexer: isIndexer,
LongLivedStreamRecordedService: common.NewStreamRecordedService[interface{}](h, 1000),
activeSearches: map[string]*activeSearch{},
peerSearches: common.NewSearchTracker(),
}
// Register the bandwidth probe handler so any peer measuring this node's
// throughput can open a dedicated probe stream and read the echo.
@@ -204,32 +197,21 @@ func (d *Node) publishPeerRecord(
}
// SearchPeerRecord starts a distributed peer search via ProtocolSearchPeer.
// userKey identifies the requesting user — a new call cancels any previous
// search for the same user. Results are pushed to onResult as they arrive.
// The function returns when the search stream closes (idle timeout or indexer unreachable).
// A new call for the same userKey cancels any previous search.
// Results are pushed to onResult as they arrive; the function returns when
// the stream closes (idle timeout, explicit cancel, or indexer unreachable).
func (d *Node) SearchPeerRecord(userKey, needle string, onResult func(common.SearchHit)) {
fmt.Println("SearchPeerRecord", needle)
logger := oclib.GetLogger()
idleTimeout := common.SearchIdleTimeout()
ctx, cancel := context.WithCancel(context.Background())
// Register cancels any previous search for userKey and starts the idle timer.
// The composite key doubles as QueryID so the indexer echoes it back.
searchKey := d.peerSearches.Register(userKey, cancel, idleTimeout)
defer d.peerSearches.Cancel(searchKey)
d.activeSearchesMu.Lock()
if prev, ok := d.activeSearches[userKey]; ok {
prev.cancel()
}
queryID := uuid.New().String()
d.activeSearches[userKey] = &activeSearch{queryID: queryID, cancel: cancel}
d.activeSearchesMu.Unlock()
defer func() {
cancel()
d.activeSearchesMu.Lock()
if cur, ok := d.activeSearches[userKey]; ok && cur.queryID == queryID {
delete(d.activeSearches, userKey)
}
d.activeSearchesMu.Unlock()
}()
req := common.SearchPeerRequest{QueryID: queryID}
req := common.SearchPeerRequest{QueryID: searchKey}
if pid, err := pp.Decode(needle); err == nil {
req.PeerID = pid.String()
} else if _, err := uuid.Parse(needle); err == nil {
@@ -238,7 +220,6 @@ func (d *Node) SearchPeerRecord(userKey, needle string, onResult func(common.Sea
req.Name = needle
}
// Try indexers in pool order until one accepts the stream.
for _, ad := range common.Indexers.GetAddrs() {
if ad.Info == nil {
continue
@@ -253,15 +234,22 @@ func (d *Node) SearchPeerRecord(userKey, needle string, onResult func(common.Sea
s.Reset()
continue
}
// Interrupt the blocking Decode as soon as the context is cancelled
// (idle timer, explicit PB_CLOSE_SEARCH, or replacement search).
go func() {
<-ctx.Done()
s.SetReadDeadline(time.Now())
}()
dec := json.NewDecoder(s)
for {
var result common.SearchPeerResult
if err := dec.Decode(&result); err != nil {
break
}
if result.QueryID != queryID {
continue // stale response from a previous query
if result.QueryID != searchKey || !d.peerSearches.IsActive(searchKey) {
break
}
d.peerSearches.ResetIdle(searchKey)
for _, hit := range result.Records {
onResult(hit)
}