go.etcd.io/etcd@v3.3.27+incompatible/clientv3/mirror/syncer.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package mirror implements etcd mirroring operations.
    16  package mirror
    17  
    18  import (
    19  	"context"
    20  
    21  	"github.com/coreos/etcd/clientv3"
    22  )
    23  
    24  const (
    25  	batchLimit = 1000
    26  )
    27  
    28  // Syncer syncs with the key-value state of an etcd cluster.
    29  type Syncer interface {
    30  	// SyncBase syncs the base state of the key-value state.
    31  	// The key-value state are sent through the returned chan.
    32  	SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error)
    33  	// SyncUpdates syncs the updates of the key-value state.
    34  	// The update events are sent through the returned chan.
    35  	SyncUpdates(ctx context.Context) clientv3.WatchChan
    36  }
    37  
    38  // NewSyncer creates a Syncer.
    39  func NewSyncer(c *clientv3.Client, prefix string, rev int64) Syncer {
    40  	return &syncer{c: c, prefix: prefix, rev: rev}
    41  }
    42  
    43  type syncer struct {
    44  	c      *clientv3.Client
    45  	rev    int64
    46  	prefix string
    47  }
    48  
    49  func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error) {
    50  	respchan := make(chan clientv3.GetResponse, 1024)
    51  	errchan := make(chan error, 1)
    52  
    53  	// if rev is not specified, we will choose the most recent revision.
    54  	if s.rev == 0 {
    55  		resp, err := s.c.Get(ctx, "foo")
    56  		if err != nil {
    57  			errchan <- err
    58  			close(respchan)
    59  			close(errchan)
    60  			return respchan, errchan
    61  		}
    62  		s.rev = resp.Header.Revision
    63  	}
    64  
    65  	go func() {
    66  		defer close(respchan)
    67  		defer close(errchan)
    68  
    69  		var key string
    70  
    71  		opts := []clientv3.OpOption{clientv3.WithLimit(batchLimit), clientv3.WithRev(s.rev)}
    72  
    73  		if len(s.prefix) == 0 {
    74  			// If len(s.prefix) == 0, we will sync the entire key-value space.
    75  			// We then range from the smallest key (0x00) to the end.
    76  			opts = append(opts, clientv3.WithFromKey())
    77  			key = "\x00"
    78  		} else {
    79  			// If len(s.prefix) != 0, we will sync key-value space with given prefix.
    80  			// We then range from the prefix to the next prefix if exists. Or we will
    81  			// range from the prefix to the end if the next prefix does not exists.
    82  			opts = append(opts, clientv3.WithRange(clientv3.GetPrefixRangeEnd(s.prefix)))
    83  			key = s.prefix
    84  		}
    85  
    86  		for {
    87  			resp, err := s.c.Get(ctx, key, opts...)
    88  			if err != nil {
    89  				errchan <- err
    90  				return
    91  			}
    92  
    93  			respchan <- (clientv3.GetResponse)(*resp)
    94  
    95  			if !resp.More {
    96  				return
    97  			}
    98  			// move to next key
    99  			key = string(append(resp.Kvs[len(resp.Kvs)-1].Key, 0))
   100  		}
   101  	}()
   102  
   103  	return respchan, errchan
   104  }
   105  
   106  func (s *syncer) SyncUpdates(ctx context.Context) clientv3.WatchChan {
   107  	if s.rev == 0 {
   108  		panic("unexpected revision = 0. Calling SyncUpdates before SyncBase finishes?")
   109  	}
   110  	return s.c.Watch(ctx, s.prefix, clientv3.WithPrefix(), clientv3.WithRev(s.rev+1))
   111  }