adds test for correct download with non-critical amount of nodes offline (#1574)

This commit is contained in:
Natalie Villasana 2019-03-26 14:09:44 -04:00 committed by GitHub
parent 02e07c8c65
commit 5b48a48a79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 6 deletions

View File

@ -13,6 +13,9 @@ import (
"storj.io/storj/internal/memory" "storj.io/storj/internal/memory"
"storj.io/storj/internal/testcontext" "storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testplanet" "storj.io/storj/internal/testplanet"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink"
) )
func TestUploadDownload(t *testing.T) { func TestUploadDownload(t *testing.T) {
@ -37,3 +40,72 @@ func TestUploadDownload(t *testing.T) {
assert.Equal(t, expectedData, data) assert.Equal(t, expectedData, data)
} }
func TestDownloadWithSomeNodesOffline(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 5, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
// first, upload some remote data
ul := planet.Uplinks[0]
satellite := planet.Satellites[0]
testData := make([]byte, 1*memory.MiB)
_, err := rand.Read(testData)
require.NoError(t, err)
err = ul.UploadWithConfig(ctx, satellite, &uplink.RSConfig{
MinThreshold: 2,
RepairThreshold: 3,
SuccessThreshold: 4,
MaxThreshold: 5,
}, "testbucket", "test/path", testData)
require.NoError(t, err)
// get a remote segment from pointerdb
pdb := satellite.Metainfo.Service
listResponse, _, err := pdb.List("", "", "", true, 0, 0)
require.NoError(t, err)
var path string
var pointer *pb.Pointer
for _, v := range listResponse {
path = v.GetPath()
pointer, err = pdb.Get(path)
require.NoError(t, err)
if pointer.GetType() == pb.Pointer_REMOTE {
break
}
}
// calculate how many storagenodes to kill
redundancy := pointer.GetRemote().GetRedundancy()
remotePieces := pointer.GetRemote().GetRemotePieces()
minReq := redundancy.GetMinReq()
numPieces := len(remotePieces)
toKill := numPieces - int(minReq)
nodesToKill := make(map[storj.NodeID]bool)
for i, piece := range remotePieces {
if i >= toKill {
continue
}
nodesToKill[piece.NodeId] = true
}
for _, node := range planet.StorageNodes {
if nodesToKill[node.ID()] {
err = planet.StopPeer(node)
require.NoError(t, err)
err = satellite.Overlay.Service.Delete(ctx, node.ID())
require.NoError(t, err)
}
}
// we should be able to download data without any of the original nodes
newData, err := ul.Download(ctx, satellite, "testbucket", "test/path")
require.NoError(t, err)
require.Equal(t, testData, newData)
})
}

View File

@ -24,7 +24,7 @@ const (
var ErrEmptyNode = errs.New("empty node ID") var ErrEmptyNode = errs.New("empty node ID")
// ErrNodeNotFound is returned if a node does not exist in database // ErrNodeNotFound is returned if a node does not exist in database
var ErrNodeNotFound = errs.New("Node not found") var ErrNodeNotFound = errs.Class("Node not found")
// ErrBucketNotFound is returned if a bucket is unable to be found in the routing table // ErrBucketNotFound is returned if a bucket is unable to be found in the routing table
var ErrBucketNotFound = errs.New("Bucket not found") var ErrBucketNotFound = errs.New("Bucket not found")

View File

@ -71,7 +71,7 @@ func testCache(ctx context.Context, t *testing.T, store overlay.DB) {
invalid2, err := cache.Get(ctx, missingID) invalid2, err := cache.Get(ctx, missingID)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, err == overlay.ErrNodeNotFound) assert.True(t, overlay.ErrNodeNotFound.Has(err))
assert.Nil(t, invalid2) assert.Nil(t, invalid2)
// TODO: add erroring database test // TODO: add erroring database test
@ -130,7 +130,7 @@ func testCache(ctx context.Context, t *testing.T, store overlay.DB) {
deleted, err := cache.Get(ctx, valid1ID) deleted, err := cache.Get(ctx, valid1ID)
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, deleted) assert.Nil(t, deleted)
assert.True(t, err == overlay.ErrNodeNotFound) assert.True(t, overlay.ErrNodeNotFound.Has(err))
// Test idempotent delete / non existent key delete // Test idempotent delete / non existent key delete
err = cache.Delete(ctx, valid1ID) err = cache.Delete(ctx, valid1ID)

View File

@ -315,6 +315,7 @@ func (endpoint *Endpoint) createOrderLimitsForSegment(ctx context.Context, point
pieceSize := eestream.CalcPieceSize(pointer.GetSegmentSize(), redundancy) pieceSize := eestream.CalcPieceSize(pointer.GetSegmentSize(), redundancy)
expiration := pointer.ExpirationDate expiration := pointer.ExpirationDate
var combinedErrs error
var limits []*pb.AddressedOrderLimit var limits []*pb.AddressedOrderLimit
for _, piece := range pointer.GetRemote().GetRemotePieces() { for _, piece := range pointer.GetRemote().GetRemotePieces() {
derivedPieceID := rootPieceID.Derive(piece.NodeId) derivedPieceID := rootPieceID.Derive(piece.NodeId)
@ -325,7 +326,9 @@ func (endpoint *Endpoint) createOrderLimitsForSegment(ctx context.Context, point
node, err := endpoint.cache.Get(ctx, piece.NodeId) node, err := endpoint.cache.Get(ctx, piece.NodeId)
if err != nil { if err != nil {
return nil, err endpoint.log.Error("error getting node from overlay cache", zap.Error(err))
combinedErrs = errs.Combine(combinedErrs, err)
continue
} }
if node != nil { if node != nil {
@ -336,8 +339,13 @@ func (endpoint *Endpoint) createOrderLimitsForSegment(ctx context.Context, point
Limit: orderLimit, Limit: orderLimit,
StorageNodeAddress: node.Address, StorageNodeAddress: node.Address,
}) })
} }
if len(limits) < redundancy.RequiredCount() {
err = Error.New("not enough nodes available: got %d, required %d", len(limits), redundancy.RequiredCount())
return nil, errs.Combine(combinedErrs, err)
}
return limits, nil return limits, nil
} }

View File

@ -116,7 +116,7 @@ func (cache *overlaycache) Get(ctx context.Context, id storj.NodeID) (*pb.Node,
dbx.OverlayCacheNode_NodeId(id.Bytes()), dbx.OverlayCacheNode_NodeId(id.Bytes()),
) )
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, overlay.ErrNodeNotFound return nil, overlay.ErrNodeNotFound.New("couldn't find nodeID: %s", id.String())
} }
if err != nil { if err != nil {
return nil, err return nil, err