Discovery Nano the light version.

This commit is contained in:
mr
2026-04-29 07:41:00 +02:00
parent fa341494d9
commit 7f951afd41
34 changed files with 2961 additions and 1501 deletions

View File

@@ -12,6 +12,7 @@ import (
"time"
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/config"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/utils"
@@ -42,6 +43,7 @@ var protocols = map[protocol.ID]*common.ProtocolInfo{
ProtocolVerifyResource: {WaitResponse: true, TTL: 1 * time.Minute},
ProtocolMinioConfigResource: {WaitResponse: true, TTL: 1 * time.Minute},
ProtocolAdmiraltyConfigResource: {WaitResponse: true, TTL: 1 * time.Minute},
ProtocolObserve: {WaitResponse: true, TTL: 1 * time.Minute},
}
var protocolsPartners = map[protocol.ID]*common.ProtocolInfo{
@@ -61,6 +63,21 @@ type StreamService struct {
// IsPeerKnown, when set, is called at stream open for every inbound protocol.
// Return false to reset the stream immediately. Left nil until wired by the node.
IsPeerKnown func(pid pp.ID) bool
// dnt is the Disconnection Network Tolerance cache for outbound streams.
dnt *dntCache
// observeCache tracks running heartbeat goroutines on the OBSERVED side.
observeCache *observeCache
// hbBatcher accumulates incoming heartbeats (observing side) and flushes
// them as a single NATS batch after observeBatchWindow.
hbBatcher *heartbeatBatcher
// drainUntil / drainMu implement the startup drain window: for 30 s after a
// close-all, incoming ProtocolObserve requests are rejected so stale heartbeats
// from a previous run cannot mix with fresh observations.
drainUntil time.Time
drainMu sync.RWMutex
// observeUsers tracks which users are observing which peers so streams are
// closed only when the last observer for a peer disconnects.
observeUsers *userPeerIndex
}
func InitStream(ctx context.Context, h host.Host, key pp.ID, maxNode int, node common.DiscoveryPeer) (*StreamService, error) {
@@ -72,31 +89,60 @@ func InitStream(ctx context.Context, h host.Host, key pp.ID, maxNode int, node c
Streams: common.ProtocolStream{},
maxNodesConn: maxNode,
ResourceSearches: common.NewSearchTracker(),
dnt: newDNTCache(),
observeCache: newObserveCache(),
observeUsers: newUserPeerIndex(),
}
service.hbBatcher = newHeartbeatBatcher(flushObserveBatch)
for proto := range protocols {
service.Host.SetStreamHandler(proto, service.gate(service.HandleResponse))
}
// ProtocolObserve uses a dedicated handler (bidirectional, long-lived).
logger.Info().Msg("connect to partners...")
service.connectToPartners() // we set up a stream
go service.StartGC(8 * time.Second)
go service.startDNTLoop()
return service, nil
}
// gate wraps a stream handler with IsPeerKnown validation.
// If the peer is unknown the entire connection is closed and the handler is not called.
// IsPeerKnown is read at stream-open time so it works even when set after InitStream.
func (s *StreamService) gatePrivilege(h func(network.Stream)) func(network.Stream) {
return func(stream network.Stream) {
if config.GetConfig().IsNano {
d := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil).Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"relation": {{Operator: dbs.EQUAL.String(), Value: peer.MASTER}},
},
}, "", false, 0, 1)
if len(d.Data) == 0 {
return
}
}
s.knowingGate(stream, h)
}
}
// gate wraps a stream handler with IsPeerKnown validation.
// If the peer is unknown the entire connection is closed and the handler is not called.
// IsPeerKnown is read at stream-open time so it works even when set after InitStream.
func (s *StreamService) gate(h func(network.Stream)) func(network.Stream) {
return func(stream network.Stream) {
if s.IsPeerKnown != nil && !s.IsPeerKnown(stream.Conn().RemotePeer()) {
logger := oclib.GetLogger()
logger.Warn().Str("peer", stream.Conn().RemotePeer().String()).Msg("[stream] unknown peer, closing connection")
stream.Conn().Close()
return
}
h(stream)
s.knowingGate(stream, h)
}
}
func (s *StreamService) knowingGate(stream network.Stream, h func(network.Stream)) {
if s.IsPeerKnown != nil && !s.IsPeerKnown(stream.Conn().RemotePeer()) {
logger := oclib.GetLogger()
logger.Warn().Str("peer", stream.Conn().RemotePeer().String()).Msg("[stream] unknown peer, closing connection")
stream.Conn().Close()
return
}
h(stream)
}
func (s *StreamService) HandleResponse(stream network.Stream) {
s.Mu.Lock()
defer s.Mu.Unlock()
@@ -137,13 +183,27 @@ func (s *StreamService) connectToPartners() error {
go s.readLoop(s.Streams[proto][ss.Conn().RemotePeer()], ss.Conn().RemotePeer(), proto, info)
}
logger.Info().Msg("SetStreamHandler " + string(proto))
s.Host.SetStreamHandler(proto, s.gate(f))
s.Host.SetStreamHandler(proto, s.gatePrivilege(f))
}
return nil
}
func (s *StreamService) searchPeer(search string) ([]*peer.Peer, error) {
ps := []*peer.Peer{}
if conf.GetConfig().NanoIDS != "" {
for _, peerID := range strings.Split(conf.GetConfig().NanoIDS, ",") {
ppID := strings.Split(peerID, "/")
ps = append(ps, &peer.Peer{
AbstractObject: utils.AbstractObject{
UUID: uuid.New().String(),
Name: ppID[1],
},
PeerID: ppID[len(ppID)-1],
StreamAddress: peerID,
Relation: peer.NANO,
})
}
}
if conf.GetConfig().PeerIDS != "" {
for _, peerID := range strings.Split(conf.GetConfig().PeerIDS, ",") {
ppID := strings.Split(peerID, "/")
@@ -159,7 +219,7 @@ func (s *StreamService) searchPeer(search string) ([]*peer.Peer, error) {
}
}
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil)
peers := access.Search(nil, search, false)
peers := access.Search(nil, search, false, 0, 0)
for _, p := range peers.Data {
ps = append(ps, p.(*peer.Peer))
}
@@ -230,7 +290,7 @@ func (ps *StreamService) readLoop(s *common.Stream, id pp.ID, proto protocol.ID,
}
continue
}
ps.handleEvent(evt.Type, &evt)
ps.handleEvent(evt.Type, &evt, s.Stream)
if protocolInfo.WaitResponse && !protocolInfo.PersistantStream {
break
}