mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 08:19:07 +00:00
Remove peer discovery from client; improve bootstrap and monitoring
This commit is contained in:
parent
138644aebd
commit
170b06b213
@ -18,7 +18,6 @@ import (
|
||||
libp2ppubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
|
||||
"git.debros.io/DeBros/network/pkg/anyoneproxy"
|
||||
"git.debros.io/DeBros/network/pkg/discovery"
|
||||
"git.debros.io/DeBros/network/pkg/pubsub"
|
||||
"git.debros.io/DeBros/network/pkg/storage"
|
||||
)
|
||||
@ -38,9 +37,6 @@ type Client struct {
|
||||
network *NetworkInfoImpl
|
||||
pubsub *pubSubBridge
|
||||
|
||||
// Managers
|
||||
discoveryMgr *discovery.Manager
|
||||
|
||||
// State
|
||||
connected bool
|
||||
startTime time.Time
|
||||
@ -211,20 +207,11 @@ func (c *Client) Connect() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize discovery manager (discovery.NewManager accepts a second parameter for compatibility;
|
||||
// the value is ignored by the new implementation)
|
||||
c.discoveryMgr = discovery.NewManager(c.host, nil, c.logger)
|
||||
// Client is a lightweight P2P participant - no discovery needed
|
||||
// We only connect to known bootstrap peers and let nodes handle discovery
|
||||
c.logger.Debug("Client configured as lightweight P2P participant (no discovery)")
|
||||
|
||||
// Start peer discovery
|
||||
discoveryConfig := discovery.Config{
|
||||
DiscoveryInterval: 5 * time.Second, // More frequent discovery
|
||||
MaxConnections: 10, // Allow more connections
|
||||
}
|
||||
if err := c.discoveryMgr.Start(discoveryConfig); err != nil {
|
||||
c.logger.Warn("Failed to start peer discovery", zap.Error(err))
|
||||
}
|
||||
|
||||
// Start connection monitoring
|
||||
// Start minimal connection monitoring
|
||||
c.startConnectionMonitoring()
|
||||
|
||||
c.connected = true
|
||||
@ -243,11 +230,6 @@ func (c *Client) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop peer discovery
|
||||
if c.discoveryMgr != nil {
|
||||
c.discoveryMgr.Stop()
|
||||
}
|
||||
|
||||
// Close pubsub adapter
|
||||
if c.pubsub != nil && c.pubsub.adapter != nil {
|
||||
if err := c.pubsub.adapter.Close(); err != nil {
|
||||
|
@ -19,39 +19,28 @@ func (c *Client) connectToBootstrap(ctx context.Context, addr string) error {
|
||||
// Try to extract peer info if it's a full multiaddr with peer ID
|
||||
peerInfo, err := peer.AddrInfoFromP2pAddr(ma)
|
||||
if err != nil {
|
||||
// If there's no peer ID, try to discover the peer at this address
|
||||
return c.connectToAddress(ctx, ma)
|
||||
}
|
||||
|
||||
// Avoid dialing ourselves: if the bootstrap address resolves to our own peer ID, skip.
|
||||
c.logger.Debug(string(peerInfo.ID))
|
||||
c.logger.Debug(string(c.host.ID()))
|
||||
if c.host != nil && peerInfo.ID == c.host.ID() {
|
||||
c.logger.Debug("Skipping bootstrap address because it resolves to self",
|
||||
zap.String("addr", addr),
|
||||
zap.String("peer", peerInfo.ID.String()))
|
||||
// If there's no peer ID, we can't connect
|
||||
c.logger.Warn("Bootstrap address missing peer ID, skipping",
|
||||
zap.String("addr", addr))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Avoid dialing ourselves: if the bootstrap address resolves to our own peer ID, skip.
|
||||
if c.host != nil && peerInfo.ID == c.host.ID() {
|
||||
c.logger.Debug("Skipping bootstrap address because it resolves to self",
|
||||
zap.String("addr", addr),
|
||||
zap.String("peer_id", peerInfo.ID.String()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attempt connection
|
||||
if err := c.host.Connect(ctx, *peerInfo); err != nil {
|
||||
return fmt.Errorf("failed to connect to peer: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Debug("Connected to bootstrap peer",
|
||||
zap.String("peer", peerInfo.ID.String()),
|
||||
zap.String("peer_id", peerInfo.ID.String()),
|
||||
zap.String("addr", addr))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// connectToAddress attempts to discover and connect to a peer at the given address
|
||||
func (c *Client) connectToAddress(ctx context.Context, ma multiaddr.Multiaddr) error {
|
||||
// For the simple case, we'll just warn and continue
|
||||
// In a production environment, you'd implement proper peer discovery
|
||||
|
||||
c.logger.Warn("No peer ID provided in address, skipping bootstrap connection",
|
||||
zap.String("addr", ma.String()),
|
||||
zap.String("suggestion", "Use full multiaddr with peer ID like: /ip4/127.0.0.1/tcp/4001/p2p/<peer-id>"))
|
||||
|
||||
return nil // Don't fail - let the client continue without bootstrap
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -9,31 +10,38 @@ import (
|
||||
)
|
||||
|
||||
// DefaultBootstrapPeers returns the library's default bootstrap peer multiaddrs.
|
||||
// These can be overridden by environment variables or config.
|
||||
func DefaultBootstrapPeers() []string {
|
||||
var cfg *config.Config
|
||||
return cfg.Discovery.BootstrapPeers
|
||||
}
|
||||
|
||||
// truthy reports if s is a common truthy string.
|
||||
func truthy(s string) bool {
|
||||
switch s {
|
||||
case "1", "true", "TRUE", "True", "yes", "YES", "on", "ON":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
// Check environment variable first
|
||||
if envPeers := os.Getenv("DEBROS_BOOTSTRAP_PEERS"); envPeers != "" {
|
||||
return splitCSVOrSpace(envPeers)
|
||||
}
|
||||
|
||||
// Return defaults from config package
|
||||
defaultCfg := config.DefaultConfig()
|
||||
return defaultCfg.Discovery.BootstrapPeers
|
||||
}
|
||||
|
||||
// DefaultDatabaseEndpoints returns default DB HTTP endpoints derived from default bootstrap peers.
|
||||
// Port defaults to RQLite HTTP 5001, or RQLITE_PORT if set.
|
||||
// DefaultDatabaseEndpoints returns default DB HTTP endpoints.
|
||||
// These can be overridden by environment variables or config.
|
||||
func DefaultDatabaseEndpoints() []string {
|
||||
var cfg *config.Config
|
||||
peers := DefaultBootstrapPeers()
|
||||
port := cfg.Database.RQLitePort
|
||||
if len(peers) == 0 {
|
||||
return []string{"http://localhost:" + strconv.Itoa(cfg.Database.RQLitePort)}
|
||||
// Check environment variable first
|
||||
if envNodes := os.Getenv("RQLITE_NODES"); envNodes != "" {
|
||||
return normalizeEndpoints(splitCSVOrSpace(envNodes))
|
||||
}
|
||||
|
||||
// Get default port from environment or use port from config
|
||||
defaultCfg := config.DefaultConfig()
|
||||
port := defaultCfg.Database.RQLitePort
|
||||
if envPort := os.Getenv("RQLITE_PORT"); envPort != "" {
|
||||
if p, err := strconv.Atoi(envPort); err == nil && p > 0 {
|
||||
port = p
|
||||
}
|
||||
}
|
||||
|
||||
// Try to derive from bootstrap peers if available
|
||||
peers := DefaultBootstrapPeers()
|
||||
if len(peers) > 0 {
|
||||
endpoints := make([]string, 0, len(peers))
|
||||
for _, s := range peers {
|
||||
ma, err := multiaddr.NewMultiaddr(s)
|
||||
@ -42,12 +50,11 @@ func DefaultDatabaseEndpoints() []string {
|
||||
}
|
||||
endpoints = append(endpoints, endpointFromMultiaddr(ma, port))
|
||||
}
|
||||
|
||||
out := dedupeStrings(endpoints)
|
||||
if len(out) == 0 {
|
||||
out = []string{"http://localhost:" + strconv.Itoa(port)}
|
||||
return dedupeStrings(endpoints)
|
||||
}
|
||||
return out
|
||||
|
||||
// Fallback to localhost
|
||||
return []string{"http://localhost:" + strconv.Itoa(port)}
|
||||
}
|
||||
|
||||
// MapAddrsToDBEndpoints converts a set of peer multiaddrs to DB HTTP endpoints using dbPort.
|
||||
@ -62,8 +69,10 @@ func MapAddrsToDBEndpoints(addrs []multiaddr.Multiaddr, dbPort int) []string {
|
||||
return dedupeStrings(eps)
|
||||
}
|
||||
|
||||
// endpointFromMultiaddr extracts host from multiaddr and creates HTTP endpoint
|
||||
func endpointFromMultiaddr(ma multiaddr.Multiaddr, port int) string {
|
||||
var host string
|
||||
|
||||
// Prefer DNS if present, then IP
|
||||
if v, err := ma.ValueForProtocol(multiaddr.P_DNS); err == nil && v != "" {
|
||||
host = v
|
||||
@ -85,28 +94,88 @@ func endpointFromMultiaddr(ma multiaddr.Multiaddr, port int) string {
|
||||
}
|
||||
if host == "" {
|
||||
if v, err := ma.ValueForProtocol(multiaddr.P_IP6); err == nil && v != "" {
|
||||
host = v
|
||||
host = "[" + v + "]" // IPv6 needs brackets in URLs
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
return "http://" + host + ":" + strconv.Itoa(port)
|
||||
}
|
||||
|
||||
func dedupeStrings(in []string) []string {
|
||||
m := make(map[string]struct{}, len(in))
|
||||
// normalizeEndpoints ensures each endpoint has an http scheme and a port (defaults to 5001)
|
||||
func normalizeEndpoints(in []string) []string {
|
||||
out := make([]string, 0, len(in))
|
||||
for _, s := range in {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := m[s]; ok {
|
||||
continue
|
||||
|
||||
// Prepend scheme if missing
|
||||
if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") {
|
||||
s = "http://" + s
|
||||
}
|
||||
m[s] = struct{}{}
|
||||
|
||||
// Simple check for port (doesn't handle all cases but good enough)
|
||||
if !strings.Contains(s, ":5001") && !strings.Contains(s, ":500") && !strings.Contains(s, ":501") {
|
||||
// Check if there's already a port after the host
|
||||
parts := strings.Split(s, "://")
|
||||
if len(parts) == 2 {
|
||||
hostPart := parts[1]
|
||||
// Count colons to detect port (simple heuristic)
|
||||
colonCount := strings.Count(hostPart, ":")
|
||||
if colonCount == 0 || (strings.Contains(hostPart, "[") && colonCount == 1) {
|
||||
// No port found, add default
|
||||
s = s + ":5001"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// dedupeStrings removes duplicate strings from slice
|
||||
func dedupeStrings(in []string) []string {
|
||||
if len(in) == 0 {
|
||||
return in
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(in))
|
||||
out := make([]string, 0, len(in))
|
||||
|
||||
for _, s := range in {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[s]; ok {
|
||||
continue
|
||||
}
|
||||
seen[s] = struct{}{}
|
||||
out = append(out, s)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// splitCSVOrSpace splits a string by commas or spaces
|
||||
func splitCSVOrSpace(s string) []string {
|
||||
// Replace commas with spaces, then split on spaces
|
||||
s = strings.ReplaceAll(s, ",", " ")
|
||||
fields := strings.Fields(s)
|
||||
return fields
|
||||
}
|
||||
|
||||
// truthy reports if s is a common truthy string
|
||||
func truthy(s string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(s)) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -187,30 +186,7 @@ func (d *DatabaseClientImpl) getRQLiteNodes() []string {
|
||||
return DefaultDatabaseEndpoints()
|
||||
}
|
||||
|
||||
// normalizeEndpoints ensures each endpoint has an http scheme and a port (defaults to 5001)
|
||||
func normalizeEndpoints(in []string) []string {
|
||||
out := make([]string, 0, len(in))
|
||||
for _, s := range in {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
// Prepend scheme if missing so url.Parse handles host:port
|
||||
if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") {
|
||||
s = "http://" + s
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil || u.Host == "" {
|
||||
continue
|
||||
}
|
||||
// Ensure port present
|
||||
if h := u.Host; !hasPort(h) {
|
||||
u.Host = u.Host + ":5001"
|
||||
}
|
||||
out = append(out, u.String())
|
||||
}
|
||||
return out
|
||||
}
|
||||
// normalizeEndpoints is now imported from defaults.go
|
||||
|
||||
func hasPort(hostport string) bool {
|
||||
// cheap check for :port suffix (IPv6 with brackets handled by url.Parse earlier)
|
||||
@ -226,13 +202,6 @@ func hasPort(hostport string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func splitCSVOrSpace(s string) []string {
|
||||
// replace commas with spaces, then split on spaces
|
||||
s = strings.ReplaceAll(s, ",", " ")
|
||||
fields := strings.Fields(s)
|
||||
return fields
|
||||
}
|
||||
|
||||
// connectToAvailableNode tries to connect to any available RQLite node
|
||||
func (d *DatabaseClientImpl) connectToAvailableNode() (*gorqlite.Connection, error) {
|
||||
// Get RQLite nodes from environment or use defaults
|
||||
|
@ -2,23 +2,65 @@ package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// startConnectionMonitoring monitors connection health and logs status
|
||||
// startConnectionMonitoring starts minimal connection monitoring for the lightweight client.
|
||||
// Unlike nodes which need extensive monitoring, clients only need basic health checks.
|
||||
func (c *Client) startConnectionMonitoring() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
ticker := time.NewTicker(60 * time.Second) // Less frequent than nodes (60s vs 30s)
|
||||
defer ticker.Stop()
|
||||
|
||||
c.logger.Debug("Connection monitoring started")
|
||||
var lastPeerCount int
|
||||
firstCheck := true
|
||||
|
||||
for range ticker.C {
|
||||
if !c.isConnected() {
|
||||
c.logger.Debug("Connection monitoring stopped: client disconnected")
|
||||
return
|
||||
}
|
||||
|
||||
// Only touch network to detect issues; avoid noisy logs
|
||||
_ = c.host.Network().Peers()
|
||||
if c.host == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get current peer count
|
||||
peers := c.host.Network().Peers()
|
||||
currentPeerCount := len(peers)
|
||||
|
||||
// Only log if peer count changed or on first check
|
||||
if firstCheck || currentPeerCount != lastPeerCount {
|
||||
if currentPeerCount == 0 {
|
||||
c.logger.Warn("Client has no connected peers",
|
||||
zap.String("client_id", c.host.ID().String()))
|
||||
} else if currentPeerCount < lastPeerCount {
|
||||
c.logger.Info("Client lost peers",
|
||||
zap.Int("current_peers", currentPeerCount),
|
||||
zap.Int("previous_peers", lastPeerCount))
|
||||
} else if currentPeerCount > lastPeerCount && !firstCheck {
|
||||
c.logger.Debug("Client gained peers",
|
||||
zap.Int("current_peers", currentPeerCount),
|
||||
zap.Int("previous_peers", lastPeerCount))
|
||||
}
|
||||
|
||||
lastPeerCount = currentPeerCount
|
||||
firstCheck = false
|
||||
}
|
||||
|
||||
// Log detailed peer info at debug level occasionally (every 5 minutes)
|
||||
if time.Now().Unix()%300 == 0 && currentPeerCount > 0 {
|
||||
peerIDs := make([]string, 0, currentPeerCount)
|
||||
for _, p := range peers {
|
||||
peerIDs = append(peerIDs, p.String())
|
||||
}
|
||||
c.logger.Debug("Client peer status",
|
||||
zap.Int("peer_count", currentPeerCount),
|
||||
zap.Strings("peer_ids", peerIDs))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.logger.Debug("Lightweight connection monitoring started")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user