github.com/ethersphere/bee/v2@v2.2.0/pkg/api/status.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package api
     6  
     7  import (
     8  	"context"
     9  	"net/http"
    10  	"sort"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  	"github.com/ethersphere/bee/v2/pkg/topology"
    17  )
    18  
    19  type statusSnapshotResponse struct {
    20  	Overlay                 string  `json:"overlay"`
    21  	Proximity               uint    `json:"proximity"`
    22  	BeeMode                 string  `json:"beeMode"`
    23  	ReserveSize             uint64  `json:"reserveSize"`
    24  	ReserveSizeWithinRadius uint64  `json:"reserveSizeWithinRadius"`
    25  	PullsyncRate            float64 `json:"pullsyncRate"`
    26  	StorageRadius           uint8   `json:"storageRadius"`
    27  	ConnectedPeers          uint64  `json:"connectedPeers"`
    28  	NeighborhoodSize        uint64  `json:"neighborhoodSize"`
    29  	RequestFailed           bool    `json:"requestFailed,omitempty"`
    30  	BatchCommitment         uint64  `json:"batchCommitment"`
    31  	IsReachable             bool    `json:"isReachable"`
    32  	LastSyncedBlock         uint64  `json:"lastSyncedBlock"`
    33  }
    34  
    35  type statusResponse struct {
    36  	Snapshots []statusSnapshotResponse `json:"snapshots"`
    37  }
    38  
    39  // statusAccessHandler is a middleware that limits the number of simultaneous
    40  // status requests.
    41  func (s *Service) statusAccessHandler(h http.Handler) http.Handler {
    42  	logger := s.logger.WithName("status_access").Build()
    43  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    44  		if !s.statusSem.TryAcquire(1) {
    45  			logger.Debug("simultaneous operations not supported")
    46  			logger.Error(nil, "simultaneous operations not supported")
    47  			jsonhttp.TooManyRequests(w, "simultaneous operations not supported")
    48  			return
    49  		}
    50  		defer s.statusSem.Release(1)
    51  
    52  		h.ServeHTTP(w, r)
    53  	})
    54  }
    55  
    56  // statusGetHandler returns the current node status.
    57  func (s *Service) statusGetHandler(w http.ResponseWriter, _ *http.Request) {
    58  	logger := s.logger.WithName("get_status").Build()
    59  
    60  	if s.beeMode == DevMode {
    61  		logger.Warning("status endpoint is disabled in dev mode")
    62  		jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation)
    63  		return
    64  	}
    65  
    66  	ss, err := s.statusService.LocalSnapshot()
    67  	if err != nil {
    68  		logger.Debug("status snapshot", "error", err)
    69  		logger.Error(nil, "status snapshot")
    70  		jsonhttp.InternalServerError(w, err)
    71  		return
    72  	}
    73  
    74  	jsonhttp.OK(w, statusSnapshotResponse{
    75  		Proximity:               256,
    76  		Overlay:                 s.overlay.String(),
    77  		BeeMode:                 ss.BeeMode,
    78  		ReserveSize:             ss.ReserveSize,
    79  		ReserveSizeWithinRadius: ss.ReserveSizeWithinRadius,
    80  		PullsyncRate:            ss.PullsyncRate,
    81  		StorageRadius:           uint8(ss.StorageRadius),
    82  		ConnectedPeers:          ss.ConnectedPeers,
    83  		NeighborhoodSize:        ss.NeighborhoodSize,
    84  		BatchCommitment:         ss.BatchCommitment,
    85  		IsReachable:             ss.IsReachable,
    86  		LastSyncedBlock:         ss.LastSyncedBlock,
    87  	})
    88  }
    89  
    90  // statusGetPeersHandler returns the status of currently connected peers.
    91  func (s *Service) statusGetPeersHandler(w http.ResponseWriter, r *http.Request) {
    92  	logger := s.logger.WithName("get_status_peers").Build()
    93  
    94  	if s.beeMode == DevMode {
    95  		logger.Warning("status endpoint is disabled in dev mode")
    96  		jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation)
    97  		return
    98  	}
    99  
   100  	var (
   101  		wg        sync.WaitGroup
   102  		mu        sync.Mutex // mu protects snapshots.
   103  		snapshots []statusSnapshotResponse
   104  	)
   105  
   106  	peerFunc := func(address swarm.Address, po uint8) (bool, bool, error) {
   107  		ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
   108  
   109  		wg.Add(1)
   110  		go func() {
   111  			defer cancel()
   112  			defer wg.Done()
   113  
   114  			snapshot := statusSnapshotResponse{
   115  				Overlay:   address.String(),
   116  				Proximity: uint(po),
   117  			}
   118  
   119  			ss, err := s.statusService.PeerSnapshot(ctx, address)
   120  			if err != nil {
   121  				logger.Debug("unable to get status snapshot for peer", "peer_address", address, "error", err)
   122  				snapshot.RequestFailed = true
   123  			} else {
   124  				snapshot.BeeMode = ss.BeeMode
   125  				snapshot.ReserveSize = ss.ReserveSize
   126  				snapshot.ReserveSizeWithinRadius = ss.ReserveSizeWithinRadius
   127  				snapshot.PullsyncRate = ss.PullsyncRate
   128  				snapshot.StorageRadius = uint8(ss.StorageRadius)
   129  				snapshot.ConnectedPeers = ss.ConnectedPeers
   130  				snapshot.NeighborhoodSize = ss.NeighborhoodSize
   131  				snapshot.BatchCommitment = ss.BatchCommitment
   132  				snapshot.IsReachable = ss.IsReachable
   133  				snapshot.LastSyncedBlock = ss.LastSyncedBlock
   134  			}
   135  
   136  			mu.Lock()
   137  			snapshots = append(snapshots, snapshot)
   138  			mu.Unlock()
   139  		}()
   140  
   141  		return false, false, nil
   142  	}
   143  
   144  	err := s.topologyDriver.EachConnectedPeer(
   145  		peerFunc,
   146  		topology.Select{},
   147  	)
   148  	if err != nil {
   149  		logger.Debug("status snapshot", "error", err)
   150  		logger.Error(nil, "status snapshot")
   151  		jsonhttp.InternalServerError(w, err)
   152  		return
   153  	}
   154  
   155  	wg.Wait()
   156  
   157  	sort.Slice(snapshots, func(i, j int) bool {
   158  		return snapshots[i].Proximity < snapshots[j].Proximity
   159  	})
   160  	jsonhttp.OK(w, statusResponse{Snapshots: snapshots})
   161  }