Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
419 lines
16 KiB
Go
419 lines
16 KiB
Go
package weed_server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
"github.com/seaweedfs/seaweedfs/weed/wdclient"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func TestLookupDirectoryEntriesRejectsInvalidRequests(t *testing.T) {
|
|
valid := &filer_pb.LookupDirectoryEntryRequest{Directory: "/batch", Name: "key"}
|
|
tests := []struct {
|
|
name string
|
|
req *filer_pb.LookupDirectoryEntriesRequest
|
|
wantCode codes.Code
|
|
}{
|
|
{name: "nil request", wantCode: codes.InvalidArgument},
|
|
{name: "empty batch", req: &filer_pb.LookupDirectoryEntriesRequest{}, wantCode: codes.InvalidArgument},
|
|
{name: "oversized", req: repeatedBatchLookupRequest(maxFilerBatchLookupRequests+1, valid), wantCode: codes.ResourceExhausted},
|
|
{name: "nil item", req: &filer_pb.LookupDirectoryEntriesRequest{Requests: []*filer_pb.LookupDirectoryEntryRequest{nil}}, wantCode: codes.InvalidArgument},
|
|
{name: "relative directory", req: &filer_pb.LookupDirectoryEntriesRequest{Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "batch", Name: "key"}}}, wantCode: codes.InvalidArgument},
|
|
{name: "empty name", req: &filer_pb.LookupDirectoryEntriesRequest{Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch"}}}, wantCode: codes.InvalidArgument},
|
|
{name: "nested name", req: &filer_pb.LookupDirectoryEntriesRequest{Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "a/b"}}}, wantCode: codes.InvalidArgument},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
called := false
|
|
_, err := lookupDirectoryEntries(context.Background(), tt.req, 17,
|
|
func(context.Context, util.FullPath) (*filer.Entry, int64, error) {
|
|
called = true
|
|
return nil, 0, nil
|
|
},
|
|
func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
called = true
|
|
return nil, nil
|
|
},
|
|
)
|
|
if status.Code(err) != tt.wantCode {
|
|
t.Fatalf("error code = %v, want %v: %v", status.Code(err), tt.wantCode, err)
|
|
}
|
|
if called {
|
|
t.Fatal("invalid request reached lookup implementation")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesAcceptsMaximumBatch(t *testing.T) {
|
|
req := repeatedBatchLookupRequest(maxFilerBatchLookupRequests, &filer_pb.LookupDirectoryEntryRequest{
|
|
Directory: "/batch",
|
|
Name: "key",
|
|
})
|
|
response, err := lookupDirectoryEntries(context.Background(), req, 13,
|
|
func(context.Context, util.FullPath) (*filer.Entry, int64, error) {
|
|
return nil, 99, filer_pb.ErrNotFound
|
|
},
|
|
func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
t.Fatal("volume lookup called for an all-miss batch")
|
|
return nil, nil
|
|
},
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("maximum-sized batch failed: %v", err)
|
|
}
|
|
if got := len(response.Results); got != maxFilerBatchLookupRequests {
|
|
t.Fatalf("result count = %d, want %d", got, maxFilerBatchLookupRequests)
|
|
}
|
|
for _, index := range []int{0, maxFilerBatchLookupRequests - 1} {
|
|
if got := response.Results[index]; got.LogTsNs != 99 || got.LogSignature != 13 {
|
|
t.Fatalf("miss fence[%d] = (%d,%d), want (99,13)", index, got.LogTsNs, got.LogSignature)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesUsesFilerSignatureForFoundAndMiss(t *testing.T) {
|
|
store := newRenameTestStore()
|
|
if err := store.InsertEntry(context.Background(), newFileEntry("/batch/found", 1)); err != nil {
|
|
t.Fatalf("seed entry: %v", err)
|
|
}
|
|
testFiler := newRenameTestFiler(t, store)
|
|
testFiler.Signature = 73
|
|
server := &FilerServer{
|
|
filer: testFiler,
|
|
entryLockTable: util.NewLockTable[util.FullPath](),
|
|
}
|
|
|
|
response, err := server.LookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{
|
|
{Directory: "/batch", Name: "found"},
|
|
{Directory: "/batch", Name: "missing"},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
if !response.Results[0].Found || response.Results[1].Found {
|
|
t.Fatalf("unexpected found states: %v, %v", response.Results[0].Found, response.Results[1].Found)
|
|
}
|
|
for index, result := range response.Results {
|
|
if result.LogSignature != 73 || result.LogTsNs == 0 {
|
|
t.Fatalf("result[%d] fence = (%d,%d), want nonzero timestamp and signature 73", index, result.LogTsNs, result.LogSignature)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPopulateBatchReadAuthMintsExactFidCapabilities(t *testing.T) {
|
|
response := &filer_pb.LookupDirectoryEntriesResponse{Results: []*filer_pb.LookupDirectoryEntryResult{
|
|
{Found: true, Entry: &filer_pb.Entry{Chunks: []*filer_pb.FileChunk{
|
|
{FileId: "7,abc"}, {FileId: "8,def"}, {FileId: "7,abc"},
|
|
}}},
|
|
{Found: false},
|
|
}}
|
|
populateBatchReadAuth(response, func(fid string) string { return "jwt:" + fid })
|
|
if !reflect.DeepEqual(response.ReadAuth, map[string]string{
|
|
"7,abc": "jwt:7,abc",
|
|
"8,def": "jwt:8,def",
|
|
}) {
|
|
t.Fatalf("read auth = %#v", response.ReadAuth)
|
|
}
|
|
}
|
|
|
|
func TestPopulateBatchReadAuthDoesNotInventCapabilitiesWhenSigningIsDisabled(t *testing.T) {
|
|
response := &filer_pb.LookupDirectoryEntriesResponse{Results: []*filer_pb.LookupDirectoryEntryResult{
|
|
{Found: true, Entry: &filer_pb.Entry{Chunks: []*filer_pb.FileChunk{{FileId: "7,abc"}}}},
|
|
}}
|
|
populateBatchReadAuth(response, func(string) string { return "" })
|
|
if len(response.ReadAuth) != 0 {
|
|
t.Fatalf("read auth must be empty without a signing key: %#v", response.ReadAuth)
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesPreservesOrderBoundsParallelismAndDeduplicatesVolumes(t *testing.T) {
|
|
const requestCount = 96
|
|
requests := make([]*filer_pb.LookupDirectoryEntryRequest, requestCount)
|
|
for index := range requests {
|
|
requests[index] = &filer_pb.LookupDirectoryEntryRequest{
|
|
Directory: "/batch",
|
|
Name: fmt.Sprintf("item-%03d", index),
|
|
}
|
|
}
|
|
|
|
var active atomic.Int32
|
|
var activeMax atomic.Int32
|
|
findEntry := func(_ context.Context, path util.FullPath) (*filer.Entry, int64, error) {
|
|
index, err := strconv.Atoi(strings.TrimPrefix(string(path), "/batch/item-"))
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
current := active.Add(1)
|
|
defer active.Add(-1)
|
|
for {
|
|
previous := activeMax.Load()
|
|
if current <= previous || activeMax.CompareAndSwap(previous, current) {
|
|
break
|
|
}
|
|
}
|
|
// Reverse completion order within each wave so append-based implementations fail.
|
|
time.Sleep(time.Duration(requestCount-index) * 50 * time.Microsecond)
|
|
if index == 7 {
|
|
return nil, int64(1000 + index), errors.New("lookup failed")
|
|
}
|
|
if index == 11 {
|
|
return nil, int64(1000 + index), filer_pb.ErrNotFound
|
|
}
|
|
volumeID := uint32(index%3 + 1)
|
|
chunk := &filer_pb.FileChunk{Fid: &filer_pb.FileId{VolumeId: volumeID, FileKey: uint64(index + 1), Cookie: 17}}
|
|
if index == 5 {
|
|
chunk = &filer_pb.FileChunk{FileId: "2,0294cbb9892b"}
|
|
}
|
|
return &filer.Entry{FullPath: path, Chunks: []*filer_pb.FileChunk{chunk, chunk}}, int64(1000 + index), nil
|
|
}
|
|
|
|
volumeLookupCalls := 0
|
|
var lookedUpVolumeIDs []string
|
|
lookupVolumes := func(_ context.Context, volumeIDs []string) (map[string][]wdclient.Location, error) {
|
|
volumeLookupCalls++
|
|
lookedUpVolumeIDs = append([]string(nil), volumeIDs...)
|
|
return map[string][]wdclient.Location{
|
|
"1": {{Url: "volume-1:8080"}},
|
|
"2": {{Url: "volume-2:8080"}},
|
|
"3": {{Url: "volume-3:8080"}},
|
|
}, nil
|
|
}
|
|
|
|
const logSignature = 0x1234
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{Requests: requests}, logSignature, findEntry, lookupVolumes)
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
if got := activeMax.Load(); got <= 1 || got > maxFilerBatchLookupWorkers {
|
|
t.Fatalf("parallel workers peaked at %d, want 2..%d", got, maxFilerBatchLookupWorkers)
|
|
}
|
|
if volumeLookupCalls != 1 {
|
|
t.Fatalf("volume lookup calls = %d, want 1", volumeLookupCalls)
|
|
}
|
|
if want := []string{"1", "2", "3"}; !reflect.DeepEqual(lookedUpVolumeIDs, want) {
|
|
t.Fatalf("volume IDs = %v, want %v", lookedUpVolumeIDs, want)
|
|
}
|
|
if got := len(response.LocationsMap); got != 3 {
|
|
t.Fatalf("location map size = %d, want 3", got)
|
|
}
|
|
|
|
for index, result := range response.Results {
|
|
if result.LogTsNs != int64(1000+index) || result.LogSignature != logSignature {
|
|
t.Fatalf("result[%d] fence = (%d,%d), want (%d,%d)", index, result.LogTsNs, result.LogSignature, 1000+index, logSignature)
|
|
}
|
|
switch index {
|
|
case 7:
|
|
if result.Found || result.Entry != nil || result.Error != "lookup failed" {
|
|
t.Fatalf("error result[%d] = %+v", index, result)
|
|
}
|
|
case 11:
|
|
if result.Found || result.Entry != nil || result.Error != "" {
|
|
t.Fatalf("not-found result[%d] = %+v", index, result)
|
|
}
|
|
default:
|
|
if !result.Found || result.Entry == nil || result.Error != "" {
|
|
t.Fatalf("found result[%d] = %+v", index, result)
|
|
}
|
|
if want := fmt.Sprintf("item-%03d", index); result.Entry.Name != want {
|
|
t.Fatalf("result[%d] name = %q, want %q", index, result.Entry.Name, want)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesMarksEntriesWithUnavailableVolumes(t *testing.T) {
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "key"}},
|
|
}, 42, func(_ context.Context, path util.FullPath) (*filer.Entry, int64, error) {
|
|
return &filer.Entry{
|
|
FullPath: path,
|
|
Chunks: []*filer_pb.FileChunk{{
|
|
Fid: &filer_pb.FileId{VolumeId: 9, FileKey: 1, Cookie: 2},
|
|
}},
|
|
}, 123, nil
|
|
}, func(_ context.Context, volumeIDs []string) (map[string][]wdclient.Location, error) {
|
|
if !sort.StringsAreSorted(volumeIDs) {
|
|
t.Fatalf("volume IDs are not sorted: %v", volumeIDs)
|
|
}
|
|
return nil, errors.New("master unavailable")
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
result := response.Results[0]
|
|
if result.LogTsNs != 123 || result.LogSignature != 42 {
|
|
t.Fatalf("result fence = (%d,%d), want (123,42)", result.LogTsNs, result.LogSignature)
|
|
}
|
|
if !result.Found || result.Entry == nil {
|
|
t.Fatalf("metadata result lost when volume lookup failed: %+v", result)
|
|
}
|
|
if !strings.Contains(result.Error, "volume 9 has no locations") || !strings.Contains(result.Error, "master unavailable") {
|
|
t.Fatalf("result error = %q", result.Error)
|
|
}
|
|
locations, ok := response.LocationsMap["9"]
|
|
if !ok || locations == nil || len(locations.Locations) != 0 {
|
|
t.Fatalf("missing volume map entry = %#v, present=%v", locations, ok)
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesKeepsMalformedEntryError(t *testing.T) {
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "key"}},
|
|
}, 42, func(_ context.Context, path util.FullPath) (*filer.Entry, int64, error) {
|
|
return &filer.Entry{
|
|
FullPath: path,
|
|
Chunks: []*filer_pb.FileChunk{
|
|
{Fid: &filer_pb.FileId{VolumeId: 9, FileKey: 1, Cookie: 2}},
|
|
{FileId: "not-a-file-id"},
|
|
},
|
|
}, 123, nil
|
|
}, func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
return map[string][]wdclient.Location{}, nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
result := response.Results[0]
|
|
if !result.Found || result.Entry == nil || !strings.Contains(result.Error, "invalid file id") {
|
|
t.Fatalf("malformed entry was reported as a clean miss: %+v", result)
|
|
}
|
|
}
|
|
|
|
func cacheVolumeEntry(_ context.Context, path util.FullPath) (*filer.Entry, int64, error) {
|
|
return &filer.Entry{
|
|
FullPath: path,
|
|
Chunks: []*filer_pb.FileChunk{{
|
|
Fid: &filer_pb.FileId{VolumeId: 9, FileKey: 1, Cookie: 2},
|
|
}},
|
|
}, 123, nil
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesCanTreatRetiredCacheVolumeAsMiss(t *testing.T) {
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "key"}},
|
|
UnavailableVolumeIsMiss: true,
|
|
}, 42, cacheVolumeEntry, func(_ context.Context, volumeIDs []string) (map[string][]wdclient.Location, error) {
|
|
// The master answered and left volume 9 out.
|
|
return map[string][]wdclient.Location{}, errors.New("volume 9: volume id 9 not found")
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
result := response.Results[0]
|
|
if result.Found || result.Entry != nil || result.Error != "" {
|
|
t.Fatalf("retired cache volume did not become a clean miss: %+v", result)
|
|
}
|
|
if result.LogTsNs != 123 || result.LogSignature != 42 {
|
|
t.Fatalf("miss fence = (%d,%d), want (123,42)", result.LogTsNs, result.LogSignature)
|
|
}
|
|
if _, exists := response.LocationsMap["9"]; exists {
|
|
t.Fatalf("cache miss returned an unusable location: %#v", response.LocationsMap["9"])
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesKeepsCacheEntryWhenMasterIsUnreachable(t *testing.T) {
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "key"}},
|
|
UnavailableVolumeIsMiss: true,
|
|
}, 42, cacheVolumeEntry, func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
return nil, errors.New("master unavailable")
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
result := response.Results[0]
|
|
if !result.Found || result.Entry == nil {
|
|
t.Fatalf("unanswered lookup was reported as a miss: %+v", result)
|
|
}
|
|
if !strings.Contains(result.Error, "volume 9 has no locations") || !strings.Contains(result.Error, "master unavailable") {
|
|
t.Fatalf("result error = %q", result.Error)
|
|
}
|
|
locations, ok := response.LocationsMap["9"]
|
|
if !ok || locations == nil || len(locations.Locations) != 0 {
|
|
t.Fatalf("unresolved volume map entry = %#v, present=%v", locations, ok)
|
|
}
|
|
}
|
|
|
|
func TestLookupDirectoryEntriesKeepsMalformedEntryErrorInCacheMode(t *testing.T) {
|
|
response, err := lookupDirectoryEntries(context.Background(), &filer_pb.LookupDirectoryEntriesRequest{
|
|
Requests: []*filer_pb.LookupDirectoryEntryRequest{{Directory: "/batch", Name: "key"}},
|
|
UnavailableVolumeIsMiss: true,
|
|
}, 42, func(_ context.Context, path util.FullPath) (*filer.Entry, int64, error) {
|
|
return &filer.Entry{
|
|
FullPath: path,
|
|
Chunks: []*filer_pb.FileChunk{
|
|
{Fid: &filer_pb.FileId{VolumeId: 9, FileKey: 1, Cookie: 2}},
|
|
{FileId: "not-a-file-id"},
|
|
},
|
|
}, 123, nil
|
|
}, func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
return map[string][]wdclient.Location{}, nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("batch lookup: %v", err)
|
|
}
|
|
result := response.Results[0]
|
|
if !result.Found || result.Entry == nil || !strings.Contains(result.Error, "invalid file id") {
|
|
t.Fatalf("malformed entry was reported as a clean miss: %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestCacheBatchLookupSelectsAuthoritativeVolumeLocations(t *testing.T) {
|
|
cachedCalls, authoritativeCalls := 0, 0
|
|
cached := func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
cachedCalls++
|
|
return map[string][]wdclient.Location{"9": {{Url: "retired-volume:8080"}}}, nil
|
|
}
|
|
authoritative := func(context.Context, []string) (map[string][]wdclient.Location, error) {
|
|
authoritativeCalls++
|
|
return nil, nil
|
|
}
|
|
|
|
lookup := selectFilerBatchVolumeLookup(&filer_pb.LookupDirectoryEntriesRequest{
|
|
UnavailableVolumeIsMiss: true,
|
|
}, cached, authoritative)
|
|
locations, err := lookup(context.Background(), []string{"9"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cachedCalls != 0 || authoritativeCalls != 1 || len(locations) != 0 {
|
|
t.Fatalf("cache lookup used stale path: cached=%d authoritative=%d locations=%v", cachedCalls, authoritativeCalls, locations)
|
|
}
|
|
|
|
lookup = selectFilerBatchVolumeLookup(&filer_pb.LookupDirectoryEntriesRequest{}, cached, authoritative)
|
|
if _, err := lookup(context.Background(), []string{"9"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cachedCalls != 1 || authoritativeCalls != 1 {
|
|
t.Fatalf("ordinary lookup did not keep cached path: cached=%d authoritative=%d", cachedCalls, authoritativeCalls)
|
|
}
|
|
}
|
|
|
|
func repeatedBatchLookupRequest(count int, request *filer_pb.LookupDirectoryEntryRequest) *filer_pb.LookupDirectoryEntriesRequest {
|
|
requests := make([]*filer_pb.LookupDirectoryEntryRequest, count)
|
|
for index := range requests {
|
|
requests[index] = &filer_pb.LookupDirectoryEntryRequest{
|
|
Directory: request.Directory,
|
|
Name: request.Name,
|
|
}
|
|
}
|
|
return &filer_pb.LookupDirectoryEntriesRequest{Requests: requests}
|
|
}
|