mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
commit
2a348fb8e9
@ -858,15 +858,15 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Maintenance"
|
"Maintenance"
|
||||||
],
|
],
|
||||||
"summary": "Hash returns the hash of the local KV state for consistency checking purpose.\nThis is designed for testing; do not use this in production when there\nare ongoing transactions.",
|
"summary": "HashKV computes the hash of all MVCC keys up to a given revision.",
|
||||||
"operationId": "Hash",
|
"operationId": "HashKV",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "body",
|
"name": "body",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/etcdserverpbHashRequest"
|
"$ref": "#/definitions/etcdserverpbHashKVRequest"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -874,7 +874,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "(empty)",
|
"description": "(empty)",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/etcdserverpbHashResponse"
|
"$ref": "#/definitions/etcdserverpbHashKVResponse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1552,6 +1552,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"etcdserverpbHashKVRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"revision": {
|
||||||
|
"description": "revision is the key-value store revision for the hash operation.",
|
||||||
|
"type": "string",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"etcdserverpbHashKVResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"compact_revision": {
|
||||||
|
"description": "compact_revision is the compacted revision of key-value store when hash begins.",
|
||||||
|
"type": "string",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"description": "hash is the hash value computed from the responding member's MVCC keys up to a given revision.",
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"$ref": "#/definitions/etcdserverpbResponseHeader"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"etcdserverpbHashRequest": {
|
"etcdserverpbHashRequest": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
@ -1559,7 +1587,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"description": "hash is the hash value computed from the responding member's key-value store.",
|
"description": "hash is the hash value computed from the responding member's KV's backend.",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64"
|
"format": "int64"
|
||||||
},
|
},
|
||||||
|
@ -137,6 +137,17 @@ func (ms *maintenanceServer) Hash(ctx context.Context, r *pb.HashRequest) (*pb.H
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ms *maintenanceServer) HashKV(ctx context.Context, r *pb.HashKVRequest) (*pb.HashKVResponse, error) {
|
||||||
|
h, rev, compactRev, err := ms.kg.KV().HashByRev(r.Revision)
|
||||||
|
if err != nil {
|
||||||
|
return nil, togRPCError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &pb.HashKVResponse{Header: &pb.ResponseHeader{Revision: rev}, Hash: h, CompactRevision: compactRev}
|
||||||
|
ms.hdr.fill(resp.Header)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ms *maintenanceServer) Alarm(ctx context.Context, ar *pb.AlarmRequest) (*pb.AlarmResponse, error) {
|
func (ms *maintenanceServer) Alarm(ctx context.Context, ar *pb.AlarmRequest) (*pb.AlarmResponse, error) {
|
||||||
return ms.a.Alarm(ctx, ar)
|
return ms.a.Alarm(ctx, ar)
|
||||||
}
|
}
|
||||||
@ -203,6 +214,13 @@ func (ams *authMaintenanceServer) Hash(ctx context.Context, r *pb.HashRequest) (
|
|||||||
return ams.maintenanceServer.Hash(ctx, r)
|
return ams.maintenanceServer.Hash(ctx, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ams *authMaintenanceServer) HashKV(ctx context.Context, r *pb.HashKVRequest) (*pb.HashKVResponse, error) {
|
||||||
|
if err := ams.isAuthenticated(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ams.maintenanceServer.HashKV(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
func (ams *authMaintenanceServer) Status(ctx context.Context, ar *pb.StatusRequest) (*pb.StatusResponse, error) {
|
func (ams *authMaintenanceServer) Status(ctx context.Context, ar *pb.StatusRequest) (*pb.StatusResponse, error) {
|
||||||
return ams.maintenanceServer.Status(ctx, ar)
|
return ams.maintenanceServer.Status(ctx, ar)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
CompactionRequest
|
CompactionRequest
|
||||||
CompactionResponse
|
CompactionResponse
|
||||||
HashRequest
|
HashRequest
|
||||||
|
HashKVRequest
|
||||||
|
HashKVResponse
|
||||||
HashResponse
|
HashResponse
|
||||||
SnapshotRequest
|
SnapshotRequest
|
||||||
SnapshotResponse
|
SnapshotResponse
|
||||||
|
@ -340,6 +340,19 @@ func request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq etcdserverpb.HashKVRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||||
|
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.HashKV(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func request_Maintenance_Snapshot_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Maintenance_SnapshotClient, runtime.ServerMetadata, error) {
|
func request_Maintenance_Snapshot_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Maintenance_SnapshotClient, runtime.ServerMetadata, error) {
|
||||||
var protoReq etcdserverpb.SnapshotRequest
|
var protoReq etcdserverpb.SnapshotRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
@ -1320,6 +1333,34 @@ func RegisterMaintenanceHandler(ctx context.Context, mux *runtime.ServeMux, conn
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_Maintenance_HashKV_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
if cn, ok := w.(http.CloseNotifier); ok {
|
||||||
|
go func(done <-chan struct{}, closed <-chan bool) {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-closed:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}(ctx.Done(), cn.CloseNotify())
|
||||||
|
}
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
|
||||||
|
}
|
||||||
|
resp, md, err := request_Maintenance_HashKV_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_Maintenance_HashKV_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("POST", pattern_Maintenance_Snapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("POST", pattern_Maintenance_Snapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -1388,6 +1429,8 @@ var (
|
|||||||
|
|
||||||
pattern_Maintenance_Hash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "hash"}, ""))
|
pattern_Maintenance_Hash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "hash"}, ""))
|
||||||
|
|
||||||
|
pattern_Maintenance_HashKV_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "hash"}, ""))
|
||||||
|
|
||||||
pattern_Maintenance_Snapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "snapshot"}, ""))
|
pattern_Maintenance_Snapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "snapshot"}, ""))
|
||||||
|
|
||||||
pattern_Maintenance_MoveLeader_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "transfer-leadership"}, ""))
|
pattern_Maintenance_MoveLeader_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "maintenance", "transfer-leadership"}, ""))
|
||||||
@ -1402,6 +1445,8 @@ var (
|
|||||||
|
|
||||||
forward_Maintenance_Hash_0 = runtime.ForwardResponseMessage
|
forward_Maintenance_Hash_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_Maintenance_HashKV_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_Maintenance_Snapshot_0 = runtime.ForwardResponseStream
|
forward_Maintenance_Snapshot_0 = runtime.ForwardResponseStream
|
||||||
|
|
||||||
forward_Maintenance_MoveLeader_0 = runtime.ForwardResponseMessage
|
forward_Maintenance_MoveLeader_0 = runtime.ForwardResponseMessage
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -174,7 +174,7 @@ service Maintenance {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash of the local KV state for consistency checking purpose.
|
// Hash computes the hash of the KV's backend.
|
||||||
// This is designed for testing; do not use this in production when there
|
// This is designed for testing; do not use this in production when there
|
||||||
// are ongoing transactions.
|
// are ongoing transactions.
|
||||||
rpc Hash(HashRequest) returns (HashResponse) {
|
rpc Hash(HashRequest) returns (HashResponse) {
|
||||||
@ -184,6 +184,14 @@ service Maintenance {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashKV computes the hash of all MVCC keys up to a given revision.
|
||||||
|
rpc HashKV(HashKVRequest) returns (HashKVResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/hash"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
|
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
|
||||||
rpc Snapshot(SnapshotRequest) returns (stream SnapshotResponse) {
|
rpc Snapshot(SnapshotRequest) returns (stream SnapshotResponse) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
@ -581,9 +589,22 @@ message CompactionResponse {
|
|||||||
message HashRequest {
|
message HashRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message HashKVRequest {
|
||||||
|
// revision is the key-value store revision for the hash operation.
|
||||||
|
int64 revision = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HashKVResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// hash is the hash value computed from the responding member's MVCC keys up to a given revision.
|
||||||
|
uint32 hash = 2;
|
||||||
|
// compact_revision is the compacted revision of key-value store when hash begins.
|
||||||
|
int64 compact_revision = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message HashResponse {
|
message HashResponse {
|
||||||
ResponseHeader header = 1;
|
ResponseHeader header = 1;
|
||||||
// hash is the hash value computed from the responding member's key-value store.
|
// hash is the hash value computed from the responding member's KV's backend.
|
||||||
uint32 hash = 2;
|
uint32 hash = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,55 @@ func TestV3CompactCurrentRev(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestV3HashKV ensures that multiple calls of HashKV on same node return same hash and compact rev.
|
||||||
|
func TestV3HashKV(t *testing.T) {
|
||||||
|
defer testutil.AfterTest(t)
|
||||||
|
clus := NewClusterV3(t, &ClusterConfig{Size: 1})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
kvc := toGRPC(clus.RandClient()).KV
|
||||||
|
mvc := toGRPC(clus.RandClient()).Maintenance
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
resp, err := kvc.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte(fmt.Sprintf("bar%d", i))})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rev := resp.Header.Revision
|
||||||
|
hresp, err := mvc.HashKV(context.Background(), &pb.HashKVRequest{0})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if rev != hresp.Header.Revision {
|
||||||
|
t.Fatalf("Put rev %v != HashKV rev %v", rev, hresp.Header.Revision)
|
||||||
|
}
|
||||||
|
|
||||||
|
prevHash := hresp.Hash
|
||||||
|
prevCompactRev := hresp.CompactRevision
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
hresp, err := mvc.HashKV(context.Background(), &pb.HashKVRequest{0})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if rev != hresp.Header.Revision {
|
||||||
|
t.Fatalf("Put rev %v != HashKV rev %v", rev, hresp.Header.Revision)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevHash != hresp.Hash {
|
||||||
|
t.Fatalf("prevHash %v != Hash %v", prevHash, hresp.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevCompactRev != hresp.CompactRevision {
|
||||||
|
t.Fatalf("prevCompactRev %v != CompactRevision %v", prevHash, hresp.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
prevHash = hresp.Hash
|
||||||
|
prevCompactRev = hresp.CompactRevision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestV3TxnTooManyOps(t *testing.T) {
|
func TestV3TxnTooManyOps(t *testing.T) {
|
||||||
defer testutil.AfterTest(t)
|
defer testutil.AfterTest(t)
|
||||||
maxTxnOps := uint(128)
|
maxTxnOps := uint(128)
|
||||||
|
@ -107,10 +107,12 @@ type KV interface {
|
|||||||
// Write creates a write transaction.
|
// Write creates a write transaction.
|
||||||
Write() TxnWrite
|
Write() TxnWrite
|
||||||
|
|
||||||
// Hash retrieves the hash of KV state and revision.
|
// Hash computes the hash of the KV's backend.
|
||||||
// This method is designed for consistency checking purposes.
|
|
||||||
Hash() (hash uint32, revision int64, err error)
|
Hash() (hash uint32, revision int64, err error)
|
||||||
|
|
||||||
|
// HashByRev computes the hash of all MVCC revisions up to a given revision.
|
||||||
|
HashByRev(rev int64) (hash uint32, revision int64, compactRev int64, err error)
|
||||||
|
|
||||||
// Compact frees all superseded keys with revisions less than rev.
|
// Compact frees all superseded keys with revisions less than rev.
|
||||||
Compact(rev int64) (<-chan struct{}, error)
|
Compact(rev int64) (<-chan struct{}, error)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package mvcc
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"hash/crc32"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -44,6 +45,8 @@ var (
|
|||||||
ErrClosed = errors.New("mvcc: closed")
|
ErrClosed = errors.New("mvcc: closed")
|
||||||
|
|
||||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "mvcc")
|
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "mvcc")
|
||||||
|
|
||||||
|
emptyKeep = make(map[revision]struct{})
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -98,6 +101,12 @@ type store struct {
|
|||||||
fifoSched schedule.Scheduler
|
fifoSched schedule.Scheduler
|
||||||
|
|
||||||
stopc chan struct{}
|
stopc chan struct{}
|
||||||
|
|
||||||
|
// keepMu protects keep
|
||||||
|
keepMu sync.RWMutex
|
||||||
|
// keep contains all revisions <= compactMainRev to be kept for the
|
||||||
|
// ongoing compaction; nil otherwise.
|
||||||
|
keep map[revision]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStore returns a new store. It is useful to create a store inside
|
// NewStore returns a new store. It is useful to create a store inside
|
||||||
@ -160,6 +169,63 @@ func (s *store) Hash() (hash uint32, revision int64, err error) {
|
|||||||
return h, s.currentRev, err
|
return h, s.currentRev, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *store) HashByRev(rev int64) (hash uint32, currentRev int64, compactRev int64, err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.revMu.RLock()
|
||||||
|
compactRev, currentRev = s.compactMainRev, s.currentRev
|
||||||
|
s.revMu.RUnlock()
|
||||||
|
|
||||||
|
if rev > 0 && rev <= compactRev {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return 0, 0, compactRev, ErrCompacted
|
||||||
|
} else if rev > 0 && rev > currentRev {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return 0, currentRev, 0, ErrFutureRev
|
||||||
|
}
|
||||||
|
|
||||||
|
s.keepMu.Lock()
|
||||||
|
if s.keep == nil {
|
||||||
|
// ForceCommit ensures that txnRead begins after backend
|
||||||
|
// has committed all the changes from the prev completed compaction.
|
||||||
|
s.b.ForceCommit()
|
||||||
|
s.keep = emptyKeep
|
||||||
|
}
|
||||||
|
keep := s.keep
|
||||||
|
s.keepMu.Unlock()
|
||||||
|
|
||||||
|
tx := s.b.ReadTx()
|
||||||
|
tx.Lock()
|
||||||
|
defer tx.Unlock()
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
if rev == 0 {
|
||||||
|
rev = currentRev
|
||||||
|
}
|
||||||
|
|
||||||
|
upper := revision{main: rev + 1}
|
||||||
|
lower := revision{main: compactRev + 1}
|
||||||
|
h := crc32.New(crc32.MakeTable(crc32.Castagnoli))
|
||||||
|
|
||||||
|
h.Write(keyBucketName)
|
||||||
|
err = tx.UnsafeForEach(keyBucketName, func(k, v []byte) error {
|
||||||
|
kr := bytesToRev(k)
|
||||||
|
if !upper.GreaterThan(kr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// skip revisions that are scheduled for deletion
|
||||||
|
// due to compacting; don't skip if there isn't one.
|
||||||
|
if lower.GreaterThan(kr) && len(keep) > 0 {
|
||||||
|
if _, ok := keep[kr]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.Write(k)
|
||||||
|
h.Write(v)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return h.Sum32(), currentRev, compactRev, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *store) Compact(rev int64) (<-chan struct{}, error) {
|
func (s *store) Compact(rev int64) (<-chan struct{}, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
@ -191,6 +257,9 @@ func (s *store) Compact(rev int64) (<-chan struct{}, error) {
|
|||||||
s.b.ForceCommit()
|
s.b.ForceCommit()
|
||||||
|
|
||||||
keep := s.kvindex.Compact(rev)
|
keep := s.kvindex.Compact(rev)
|
||||||
|
s.keepMu.Lock()
|
||||||
|
s.keep = keep
|
||||||
|
s.keepMu.Unlock()
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
var j = func(ctx context.Context) {
|
var j = func(ctx context.Context) {
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
@ -202,6 +271,9 @@ func (s *store) Compact(rev int64) (<-chan struct{}, error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
close(ch)
|
close(ch)
|
||||||
|
s.keepMu.Lock()
|
||||||
|
s.keep = nil
|
||||||
|
s.keepMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.fifoSched.Schedule(j)
|
s.fifoSched.Schedule(j)
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -510,6 +511,78 @@ func TestRestoreContinueUnfinishedCompaction(t *testing.T) {
|
|||||||
t.Errorf("key for rev %+v still exists, want deleted", bytesToRev(revbytes))
|
t.Errorf("key for rev %+v still exists, want deleted", bytesToRev(revbytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hashKVResult struct {
|
||||||
|
hash uint32
|
||||||
|
compactRev int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHashKVWhenCompacting ensures that HashKV returns correct hash when compacting.
|
||||||
|
func TestHashKVWhenCompacting(t *testing.T) {
|
||||||
|
b, tmpPath := backend.NewDefaultTmpBackend()
|
||||||
|
s := NewStore(b, &lease.FakeLessor{}, nil)
|
||||||
|
defer os.Remove(tmpPath)
|
||||||
|
|
||||||
|
rev := 1000
|
||||||
|
for i := 2; i <= rev; i++ {
|
||||||
|
s.Put([]byte("foo"), []byte(fmt.Sprintf("bar%d", i)), lease.NoLease)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashCompactc := make(chan hashKVResult, 1)
|
||||||
|
|
||||||
|
donec := make(chan struct{})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
hash, _, compactRev, err := s.HashByRev(int64(rev))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-donec:
|
||||||
|
return
|
||||||
|
case hashCompactc <- hashKVResult{hash, compactRev}:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(donec)
|
||||||
|
revHash := make(map[int64]uint32)
|
||||||
|
for round := 0; round < 1000; round++ {
|
||||||
|
r := <-hashCompactc
|
||||||
|
if revHash[r.compactRev] == 0 {
|
||||||
|
revHash[r.compactRev] = r.hash
|
||||||
|
}
|
||||||
|
if r.hash != revHash[r.compactRev] {
|
||||||
|
t.Fatalf("Hashes differ (current %v) != (saved %v)", r.hash, revHash[r.compactRev])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for i := 100; i >= 0; i-- {
|
||||||
|
_, err := s.Compact(int64(rev - 1 - i))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-donec:
|
||||||
|
wg.Wait()
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
testutil.FatalStack(t, "timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTxnPut(t *testing.T) {
|
func TestTxnPut(t *testing.T) {
|
||||||
// assign arbitrary size
|
// assign arbitrary size
|
||||||
bytesN := 30
|
bytesN := 30
|
||||||
|
@ -43,6 +43,10 @@ func (s *mts2mtc) Hash(ctx context.Context, r *pb.HashRequest, opts ...grpc.Call
|
|||||||
return s.mts.Hash(ctx, r)
|
return s.mts.Hash(ctx, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *mts2mtc) HashKV(ctx context.Context, r *pb.HashKVRequest, opts ...grpc.CallOption) (*pb.HashKVResponse, error) {
|
||||||
|
return s.mts.HashKV(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *mts2mtc) MoveLeader(ctx context.Context, r *pb.MoveLeaderRequest, opts ...grpc.CallOption) (*pb.MoveLeaderResponse, error) {
|
func (s *mts2mtc) MoveLeader(ctx context.Context, r *pb.MoveLeaderRequest, opts ...grpc.CallOption) (*pb.MoveLeaderResponse, error) {
|
||||||
return s.mts.MoveLeader(ctx, r)
|
return s.mts.MoveLeader(ctx, r)
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,11 @@ func (mp *maintenanceProxy) Hash(ctx context.Context, r *pb.HashRequest) (*pb.Ha
|
|||||||
return pb.NewMaintenanceClient(conn).Hash(ctx, r)
|
return pb.NewMaintenanceClient(conn).Hash(ctx, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mp *maintenanceProxy) HashKV(ctx context.Context, r *pb.HashKVRequest) (*pb.HashKVResponse, error) {
|
||||||
|
conn := mp.client.ActiveConnection()
|
||||||
|
return pb.NewMaintenanceClient(conn).HashKV(ctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
func (mp *maintenanceProxy) Alarm(ctx context.Context, r *pb.AlarmRequest) (*pb.AlarmResponse, error) {
|
func (mp *maintenanceProxy) Alarm(ctx context.Context, r *pb.AlarmRequest) (*pb.AlarmResponse, error) {
|
||||||
conn := mp.client.ActiveConnection()
|
conn := mp.client.ActiveConnection()
|
||||||
return pb.NewMaintenanceClient(conn).Alarm(ctx, r)
|
return pb.NewMaintenanceClient(conn).Alarm(ctx, r)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user