Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add status neighborhoods endpoint #4853

Merged
merged 6 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion openapi/Swarm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ paths:
$ref: "SwarmCommon.yaml#/components/schemas/FeedType"
required: false
description: "Feed indexing scheme (default: sequence)"
- $ref: "SwarmCommon.yaml#/components/headers/SwarmOnlyRootChunkParameter"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmOnlyRootChunkParameter"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmCache"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyStrategyParameter"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyFallbackModeParameter"
Expand Down Expand Up @@ -2448,6 +2448,23 @@ paths:
default:
description: Default response.

"/status/neighborhoods":
get:
summary: Get the current neighborhoods status of this node.
tags:
- Node Status
responses:
"200":
description: Returns the neighborhoods status of this node
content:
application/json:
schema:
$ref: "SwarmCommon.yaml#/components/schemas/StatusNeighborhoodsResponse"
"400":
$ref: "SwarmCommon.yaml#/components/responses/400"
default:
description: Default response.

components:
securitySchemes:
basicAuth:
Expand Down
17 changes: 17 additions & 0 deletions openapi/SwarmCommon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,23 @@ components:
items:
$ref: "#/components/schemas/StatusSnapshotResponse"

StatusNeighborhoodResponse:
type: object
properties:
address:
type: string
martinconic marked this conversation as resolved.
Show resolved Hide resolved
chunkCount:
martinconic marked this conversation as resolved.
Show resolved Hide resolved
type: integer

StatusNeighborhoodsResponse:
type: object
properties:
stamps:
type: array
nullable: false
items:
$ref: "#/components/schemas/StatusNeighborhoodResponse"

ApiChunkInclusionProof:
type: object
properties:
Expand Down
8 changes: 8 additions & 0 deletions pkg/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,14 @@ func (s *Service) mountBusinessDebug() {
),
})

handle("/status/neighborhoods", jsonhttp.MethodHandler{
"GET": web.ChainHandlers(
httpaccess.NewHTTPAccessSuppressLogHandler(),
s.statusAccessHandler,
web.FinalHandlerFunc(s.statusGetNeighborhoods),
),
})

handle("/rchash/{depth}/{anchor1}/{anchor2}", web.ChainHandlers(
web.FinalHandler(jsonhttp.MethodHandler{
"GET": http.HandlerFunc(s.rchash),
Expand Down
49 changes: 49 additions & 0 deletions pkg/api/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package api

import (
"context"
"fmt"
"net/http"
"sort"
"sync"
Expand Down Expand Up @@ -36,6 +37,15 @@ type statusResponse struct {
Snapshots []statusSnapshotResponse `json:"snapshots"`
}

type statusNeighborhoodResponse struct {
Address string `json:"address"`
ChunkCount int `json:"chunkCount"`
}

type neighborhoodsResponse struct {
Neighborhoods []statusNeighborhoodResponse `json:"neighborhoods"`
}

// statusAccessHandler is a middleware that limits the number of simultaneous
// status requests.
func (s *Service) statusAccessHandler(h http.Handler) http.Handler {
Expand Down Expand Up @@ -159,3 +169,42 @@ func (s *Service) statusGetPeersHandler(w http.ResponseWriter, r *http.Request)
})
jsonhttp.OK(w, statusResponse{Snapshots: snapshots})
}

// statusGetHandler returns the current node status.
func (s *Service) statusGetNeighborhoods(w http.ResponseWriter, _ *http.Request) {
logger := s.logger.WithName("get_status_neighborhoods").Build()

if s.beeMode == DevMode {
logger.Warning("status neighborhoods endpoint is disabled in dev mode")
jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation)
return
}

neighborhoods := make([]statusNeighborhoodResponse, 0)

nhoods, err := s.statusService.NeighborhoodsSnapshot()
if err != nil {
logger.Debug("unable to get neighborhoods status", "error", err)
logger.Error(nil, "unable to get neighborhoods status")
jsonhttp.InternalServerError(w, "unable to get neighborhoods status")
return
}

if len(nhoods) == 0 {
jsonhttp.NotFound(w, "neighborhoods not found")
return
}

for _, n := range nhoods {
binaryAddr := ""
for _, b := range n.Address.Bytes() {
binaryAddr += fmt.Sprintf("%08b ", b)
martinconic marked this conversation as resolved.
Show resolved Hide resolved
}
neighborhoods = append(neighborhoods, statusNeighborhoodResponse{
Address: binaryAddr,
ChunkCount: n.ChunkCount,
})
}

jsonhttp.OK(w, neighborhoodsResponse{Neighborhoods: neighborhoods})
}
6 changes: 6 additions & 0 deletions pkg/api/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package api_test

import (
"context"
"net/http"
"testing"

Expand All @@ -14,6 +15,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/log"
"github.com/ethersphere/bee/v2/pkg/postage"
"github.com/ethersphere/bee/v2/pkg/status"
"github.com/ethersphere/bee/v2/pkg/storer"
"github.com/ethersphere/bee/v2/pkg/topology"
)

Expand Down Expand Up @@ -119,6 +121,7 @@ type statusSnapshotMock struct {
storageRadius uint8
commitment uint64
chainState *postage.ChainState
neighborhoods []*storer.NeighborhoodStat
}

func (m *statusSnapshotMock) SyncRate() float64 { return m.syncRate }
Expand All @@ -129,3 +132,6 @@ func (m *statusSnapshotMock) GetChainState() *postage.ChainState { return m.chai
func (m *statusSnapshotMock) ReserveSizeWithinRadius() uint64 {
return m.reserveSizeWithinRadius
}
func (m *statusSnapshotMock) NeighborhoodsStat(ctx context.Context) ([]*storer.NeighborhoodStat, error) {
return m.neighborhoods, nil
}
15 changes: 15 additions & 0 deletions pkg/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/p2p/protobuf"
"github.com/ethersphere/bee/v2/pkg/postage"
"github.com/ethersphere/bee/v2/pkg/status/internal/pb"
"github.com/ethersphere/bee/v2/pkg/storer"
"github.com/ethersphere/bee/v2/pkg/swarm"
"github.com/ethersphere/bee/v2/pkg/topology"
)
Expand All @@ -39,6 +40,7 @@ type Reserve interface {
ReserveSize() int
ReserveSizeWithinRadius() uint64
StorageRadius() uint8
NeighborhoodsStat(ctx context.Context) ([]*storer.NeighborhoodStat, error)
}

type topologyDriver interface {
Expand Down Expand Up @@ -131,6 +133,19 @@ func (s *Service) LocalSnapshot() (*Snapshot, error) {
}, nil
}

func (s *Service) NeighborhoodsSnapshot() ([]*storer.NeighborhoodStat, error) {
martinconic marked this conversation as resolved.
Show resolved Hide resolved
var err error
neighborhoods := make([]*storer.NeighborhoodStat, 0)

if s.reserve != nil {
neighborhoods, err = s.reserve.NeighborhoodsStat(context.Background())
if err != nil {
return neighborhoods, err
}
}
return neighborhoods, err
}

// PeerSnapshot sends request for status snapshot to the peer.
func (s *Service) PeerSnapshot(ctx context.Context, peer swarm.Address) (*Snapshot, error) {
stream, err := s.streamer.NewStream(ctx, peer, nil, protocolName, protocolVersion, streamName)
Expand Down
12 changes: 10 additions & 2 deletions pkg/status/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/postage"
"github.com/ethersphere/bee/v2/pkg/status"
"github.com/ethersphere/bee/v2/pkg/status/internal/pb"
"github.com/ethersphere/bee/v2/pkg/storer"
"github.com/ethersphere/bee/v2/pkg/swarm"
"github.com/ethersphere/bee/v2/pkg/topology"
"github.com/google/go-cmp/cmp"
Expand All @@ -35,7 +36,7 @@ func TestStatus(t *testing.T) {
LastSyncedBlock: 6092500,
}

sssMock := &statusSnapshotMock{want}
sssMock := &statusSnapshotMock{want, nil}

peersIterMock := new(topologyPeersIterNoopMock)

Expand Down Expand Up @@ -115,7 +116,9 @@ func TestStatusLightNode(t *testing.T) {
StorageRadius: 100, // should be ignored
BatchCommitment: 1024,
LastSyncedBlock: 6092500,
}}
},
nil,
}

peersIterMock := new(topologyPeersIterNoopMock)

Expand Down Expand Up @@ -191,6 +194,7 @@ func (m *topologyPeersIterNoopMock) IsReachable() bool {
// - SyncReporter
type statusSnapshotMock struct {
*pb.Snapshot
neighborhoods []*storer.NeighborhoodStat
}

func (m *statusSnapshotMock) SyncRate() float64 { return m.Snapshot.PullsyncRate }
Expand All @@ -203,3 +207,7 @@ func (m *statusSnapshotMock) GetChainState() *postage.ChainState {
func (m *statusSnapshotMock) ReserveSizeWithinRadius() uint64 {
return m.Snapshot.ReserveSizeWithinRadius
}

func (m *statusSnapshotMock) NeighborhoodsStat(ctx context.Context) ([]*storer.NeighborhoodStat, error) {
return m.neighborhoods, nil
}
Loading