Skip to content

Commit

Permalink
Merge pull request #16154 from CaojiamingAlan/uber_applier_test
Browse files Browse the repository at this point in the history
add tests for uber applier
  • Loading branch information
ahrtr authored Jul 6, 2023
2 parents cd6cab5 + ffe73f9 commit f4444e8
Showing 1 changed file with 275 additions and 0 deletions.
275 changes: 275 additions & 0 deletions server/etcdserver/apply/uber_applier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// Copyright 2023 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apply

import (
"testing"
"time"

"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"golang.org/x/crypto/bcrypt"

pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/server/v3/auth"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/etcdserver/errors"
"go.etcd.io/etcd/server/v3/lease"
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
"go.etcd.io/etcd/server/v3/storage/mvcc"
"go.etcd.io/etcd/server/v3/storage/schema"
)

const memberId = 111195

func defaultUberApplier(t *testing.T) UberApplier {
lg := zaptest.NewLogger(t)
be, _ := betesting.NewDefaultTmpBackend(t)
t.Cleanup(func() {
betesting.Close(t, be)
})

cluster := membership.NewCluster(lg)
cluster.AddMember(&membership.Member{ID: memberId}, true)
lessor := lease.NewLessor(lg, be, cluster, lease.LessorConfig{})
kv := mvcc.NewStore(lg, be, lessor, mvcc.StoreConfig{})
alarmStore, err := v3alarm.NewAlarmStore(lg, schema.NewAlarmBackend(lg, be))
require.NoError(t, err)

tp, err := auth.NewTokenProvider(lg, "simple", dummyIndexWaiter, 300*time.Second)
require.NoError(t, err)
authStore := auth.NewAuthStore(
lg,
schema.NewAuthBackend(lg, be),
tp,
bcrypt.DefaultCost,
)
consistentIndex := cindex.NewConsistentIndex(be)
return NewUberApplier(
lg,
be,
kv,
alarmStore,
authStore,
lessor,
cluster,
&fakeRaftStatusGetter{},
&fakeSnapshotServer{},
consistentIndex,
1*time.Hour,
false,
16*1024*1024, //16MB
)
}

// TestUberApplier_Alarm_Corrupt tests the applier returns ErrCorrupt after alarm CORRUPT is activated
func TestUberApplier_Alarm_Corrupt(t *testing.T) {
tcs := []struct {
name string
request *pb.InternalRaftRequest
expectError error
}{
{
name: "Put request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{Put: &pb.PutRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "Range request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "DeleteRange request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "Txn request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "Compaction request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "LeaseGrant request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
expectError: errors.ErrCorrupt,
},
{
name: "LeaseRevoke request returns ErrCorrupt after alarm CORRUPT is activated",
request: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}},
expectError: errors.ErrCorrupt,
},
}

ua := defaultUberApplier(t)
result := ua.Apply(&pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberId,
Alarm: pb.AlarmType_CORRUPT,
},
}, true)
require.NotNil(t, result)
require.Nil(t, result.Err)

for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
result = ua.Apply(tc.request, true)
require.NotNil(t, result)
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
})
}
}

// TestUberApplier_Alarm_Quota tests the applier returns ErrNoSpace after alarm NOSPACE is activated
func TestUberApplier_Alarm_Quota(t *testing.T) {
tcs := []struct {
name string
request *pb.InternalRaftRequest
expectError error
}{
{
name: "Put request returns ErrCorrupt after alarm NOSPACE is activated",
request: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}},
expectError: errors.ErrNoSpace,
},
{
name: "Txn request cost > 0 returns ErrCorrupt after alarm NOSPACE is activated",
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Success: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestPut{
RequestPut: &pb.PutRequest{
Key: []byte(key),
},
},
},
}}},
expectError: errors.ErrNoSpace,
},
{
name: "Txn request cost = 0 is still allowed after alarm NOSPACE is activated",
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Success: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestRange{
RequestRange: &pb.RangeRequest{
Key: []byte(key),
},
},
},
}}},
expectError: nil,
},
{
name: "Txn request cost = 0 in both branches is still allowed after alarm NOSPACE is activated",
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Compare: []*pb.Compare{
{
Key: []byte(key),
Result: pb.Compare_EQUAL,
Target: pb.Compare_CREATE,
TargetUnion: &pb.Compare_CreateRevision{CreateRevision: 0},
},
},
Success: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestRange{
RequestRange: &pb.RangeRequest{
Key: []byte(key),
},
},
},
},
Failure: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestDeleteRange{
RequestDeleteRange: &pb.DeleteRangeRequest{
Key: []byte(key),
},
},
},
}}},
expectError: nil,
},
{
name: "LeaseGrant request returns ErrCorrupt after alarm NOSPACE is activated",
request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
expectError: errors.ErrNoSpace,
},
}

ua := defaultUberApplier(t)
result := ua.Apply(&pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberId,
Alarm: pb.AlarmType_NOSPACE,
},
}, true)
require.NotNil(t, result)
require.Nil(t, result.Err)

for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
result = ua.Apply(tc.request, true)
require.NotNil(t, result)
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
})
}
}

// TestUberApplier_Alarm_Deactivate tests the applier should be able to apply after alarm is deactivated
func TestUberApplier_Alarm_Deactivate(t *testing.T) {
ua := defaultUberApplier(t)
result := ua.Apply(&pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberId,
Alarm: pb.AlarmType_NOSPACE,
},
}, true)
require.NotNil(t, result)
require.Nil(t, result.Err)

result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}}, true)
require.NotNil(t, result)
require.Equalf(t, errors.ErrNoSpace, result.Err, "Apply: got %v, expect: %v", result.Err, errors.ErrNoSpace)

result = ua.Apply(&pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_DEACTIVATE,
MemberID: memberId,
Alarm: pb.AlarmType_NOSPACE,
},
}, true)
require.NotNil(t, result)
require.Nil(t, result.Err)

result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}}, true)
require.NotNil(t, result)
require.Nil(t, result.Err)
}

0 comments on commit f4444e8

Please sign in to comment.