Simple Architecture
This commit is contained in:
@@ -21,10 +21,18 @@ import (
|
||||
"github.com/libp2p/go-libp2p"
|
||||
pubsubs "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
pp "github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
"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
|
||||
@@ -35,10 +43,14 @@ 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
|
||||
|
||||
Mu sync.RWMutex
|
||||
}
|
||||
|
||||
func InitNode(isNode bool, isIndexer bool, isNativeIndexer bool) (*Node, error) {
|
||||
func InitNode(isNode bool, isIndexer bool) (*Node, error) {
|
||||
if !isNode && !isIndexer {
|
||||
return nil, errors.New("wait... what ? your node need to at least something. Retry we can't be friend in that case")
|
||||
}
|
||||
@@ -54,13 +66,17 @@ func InitNode(isNode bool, isIndexer bool, isNativeIndexer bool) (*Node, error)
|
||||
return nil, nil
|
||||
}
|
||||
logger.Info().Msg("open a host...")
|
||||
gater := newOCConnectionGater(nil) // host set below after creation
|
||||
h, err := libp2p.New(
|
||||
libp2p.PrivateNetwork(psk),
|
||||
libp2p.Identity(priv),
|
||||
libp2p.Security(noise.ID, noise.New),
|
||||
libp2p.ListenAddrStrings(
|
||||
fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", conf.GetConfig().NodeEndpointPort),
|
||||
),
|
||||
libp2p.ConnectionGater(gater),
|
||||
)
|
||||
gater.host = h // wire host back into gater now that it exists
|
||||
logger.Info().Msg("Host open on " + h.ID().String())
|
||||
if err != nil {
|
||||
return nil, errors.New("no host no node")
|
||||
@@ -69,10 +85,15 @@ func InitNode(isNode bool, isIndexer bool, isNativeIndexer bool) (*Node, error)
|
||||
PeerID: h.ID(),
|
||||
isIndexer: isIndexer,
|
||||
LongLivedStreamRecordedService: common.NewStreamRecordedService[interface{}](h, 1000),
|
||||
activeSearches: map[string]*activeSearch{},
|
||||
}
|
||||
// Register the bandwidth probe handler so any peer measuring this node's
|
||||
// throughput can open a dedicated probe stream and read the echo.
|
||||
h.SetStreamHandler(common.ProtocolBandwidthProbe, common.HandleBandwidthProbe)
|
||||
// Register the witness query handler so peers can ask this node's view of indexers.
|
||||
h.SetStreamHandler(common.ProtocolWitnessQuery, func(s network.Stream) {
|
||||
common.HandleWitnessQuery(h, s)
|
||||
})
|
||||
var ps *pubsubs.PubSub
|
||||
if isNode {
|
||||
logger.Info().Msg("generate opencloud node...")
|
||||
@@ -104,7 +125,7 @@ func InitNode(isNode bool, isIndexer bool, isNativeIndexer bool) (*Node, error)
|
||||
return json.RawMessage(b)
|
||||
}
|
||||
logger.Info().Msg("connect to indexers...")
|
||||
common.ConnectToIndexers(node.Host, conf.GetConfig().MinIndexer, conf.GetConfig().MaxIndexer, node.PeerID, buildRecord)
|
||||
common.ConnectToIndexers(node.Host, conf.GetConfig().MinIndexer, conf.GetConfig().MaxIndexer, buildRecord)
|
||||
logger.Info().Msg("claims my node...")
|
||||
if _, err := node.claimInfo(conf.GetConfig().Name, conf.GetConfig().Hostname); err != nil {
|
||||
panic(err)
|
||||
@@ -137,7 +158,7 @@ func InitNode(isNode bool, isIndexer bool, isNativeIndexer bool) (*Node, error)
|
||||
}
|
||||
if isIndexer {
|
||||
logger.Info().Msg("generate opencloud indexer...")
|
||||
node.IndexerService = indexer.NewIndexerService(node.Host, ps, 500, isNativeIndexer)
|
||||
node.IndexerService = indexer.NewIndexerService(node.Host, ps, 500)
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
@@ -158,20 +179,14 @@ func (d *Node) publishPeerRecord(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.StreamMuIndexes.RLock()
|
||||
indexerSnapshot := make([]*pp.AddrInfo, 0, len(common.StaticIndexers))
|
||||
for _, ad := range common.StaticIndexers {
|
||||
indexerSnapshot = append(indexerSnapshot, ad)
|
||||
}
|
||||
common.StreamMuIndexes.RUnlock()
|
||||
|
||||
for _, ad := range indexerSnapshot {
|
||||
for _, ad := range common.Indexers.GetAddrs() {
|
||||
var err error
|
||||
if common.StreamIndexers, err = common.TempStream(d.Host, *ad, common.ProtocolPublish, "", common.StreamIndexers, map[protocol.ID]*common.ProtocolInfo{},
|
||||
&common.StreamMuIndexes); err != nil {
|
||||
if common.Indexers.Streams, err = common.TempStream(d.Host, *ad.Info, common.ProtocolPublish, "", common.Indexers.Streams, map[protocol.ID]*common.ProtocolInfo{},
|
||||
&common.Indexers.MuStream); err != nil {
|
||||
continue
|
||||
}
|
||||
stream := common.StreamIndexers[common.ProtocolPublish][ad.ID]
|
||||
stream := common.Indexers.Streams.GetPerID(common.ProtocolPublish, ad.Info.ID)
|
||||
base := indexer.PeerRecordPayload{
|
||||
Name: rec.Name,
|
||||
DID: rec.DID,
|
||||
@@ -188,6 +203,75 @@ func (d *Node) publishPeerRecord(
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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).
|
||||
func (d *Node) SearchPeerRecord(userKey, needle string, onResult func(common.SearchHit)) {
|
||||
logger := oclib.GetLogger()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
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}
|
||||
if pid, err := pp.Decode(needle); err == nil {
|
||||
req.PeerID = pid.String()
|
||||
} else if _, err := uuid.Parse(needle); err == nil {
|
||||
req.DID = needle
|
||||
} else {
|
||||
req.Name = needle
|
||||
}
|
||||
|
||||
// Try indexers in pool order until one accepts the stream.
|
||||
for _, ad := range common.Indexers.GetAddrs() {
|
||||
if ad.Info == nil {
|
||||
continue
|
||||
}
|
||||
dialCtx, dialCancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
s, err := d.Host.NewStream(dialCtx, ad.Info.ID, common.ProtocolSearchPeer)
|
||||
dialCancel()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if err := json.NewEncoder(s).Encode(req); err != nil {
|
||||
s.Reset()
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
for _, hit := range result.Records {
|
||||
onResult(hit)
|
||||
}
|
||||
}
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
logger.Warn().Str("user", userKey).Msg("[search] no reachable indexer for peer search")
|
||||
}
|
||||
|
||||
func (d *Node) GetPeerRecord(
|
||||
ctx context.Context,
|
||||
pidOrdid string,
|
||||
@@ -195,13 +279,6 @@ func (d *Node) GetPeerRecord(
|
||||
) ([]*peer.Peer, error) {
|
||||
var err error
|
||||
var info map[string]indexer.PeerRecord
|
||||
common.StreamMuIndexes.RLock()
|
||||
indexerSnapshot2 := make([]*pp.AddrInfo, 0, len(common.StaticIndexers))
|
||||
for _, ad := range common.StaticIndexers {
|
||||
indexerSnapshot2 = append(indexerSnapshot2, ad)
|
||||
}
|
||||
common.StreamMuIndexes.RUnlock()
|
||||
|
||||
// Build the GetValue request: if pidOrdid is neither a UUID DID nor a libp2p
|
||||
// PeerID, treat it as a human-readable name and let the indexer resolve it.
|
||||
getReq := indexer.GetValue{Key: pidOrdid}
|
||||
@@ -213,12 +290,12 @@ func (d *Node) GetPeerRecord(
|
||||
getReq.Key = ""
|
||||
}
|
||||
getReq.Search = search
|
||||
for _, ad := range indexerSnapshot2 {
|
||||
if common.StreamIndexers, err = common.TempStream(d.Host, *ad, common.ProtocolGet, "",
|
||||
common.StreamIndexers, map[protocol.ID]*common.ProtocolInfo{}, &common.StreamMuIndexes); err != nil {
|
||||
for _, ad := range common.Indexers.GetAddrs() {
|
||||
if common.Indexers.Streams, err = common.TempStream(d.Host, *ad.Info, common.ProtocolGet, "",
|
||||
common.Indexers.Streams, map[protocol.ID]*common.ProtocolInfo{}, &common.Indexers.MuStream); err != nil {
|
||||
continue
|
||||
}
|
||||
stream := common.StreamIndexers[common.ProtocolGet][ad.ID]
|
||||
stream := common.Indexers.Streams.GetPerID(common.ProtocolGet, ad.Info.ID)
|
||||
if err := json.NewEncoder(stream.Stream).Encode(getReq); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user