github.com/m3db/m3@v1.5.0/src/cluster/kv/etcd/store_test.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package etcd
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/json"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"os"
    29  	"path"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/m3db/m3/src/cluster/generated/proto/kvtest"
    34  	"github.com/m3db/m3/src/cluster/kv"
    35  	xclock "github.com/m3db/m3/src/x/clock"
    36  	"github.com/m3db/m3/src/x/retry"
    37  
    38  	"github.com/golang/protobuf/proto"
    39  	"github.com/stretchr/testify/assert"
    40  	"github.com/stretchr/testify/require"
    41  	clientv3 "go.etcd.io/etcd/client/v3"
    42  	"go.etcd.io/etcd/tests/v3/framework/integration"
    43  	"golang.org/x/net/context"
    44  )
    45  
    46  func TestValue(t *testing.T) {
    47  	v1 := newValue(nil, 2, 100)
    48  	require.Equal(t, 2, v1.Version())
    49  
    50  	v2 := newValue(nil, 1, 200)
    51  	require.Equal(t, 1, v2.Version())
    52  
    53  	require.True(t, v2.IsNewer(v1))
    54  	require.False(t, v1.IsNewer(v1))
    55  	require.False(t, v1.IsNewer(v2))
    56  }
    57  
    58  func TestGetAndSet(t *testing.T) {
    59  	ec, opts, closeFn := testStore(t)
    60  	defer closeFn()
    61  
    62  	store, err := NewStore(ec, opts)
    63  	require.NoError(t, err)
    64  
    65  	value, err := store.Get("foo")
    66  	require.Equal(t, kv.ErrNotFound, err)
    67  	require.Nil(t, value)
    68  
    69  	version, err := store.Set("foo", genProto("bar1"))
    70  	require.NoError(t, err)
    71  	require.Equal(t, 1, version)
    72  
    73  	value, err = store.Get("foo")
    74  	require.NoError(t, err)
    75  	verifyValue(t, value, "bar1", 1)
    76  
    77  	version, err = store.Set("foo", genProto("bar2"))
    78  	require.NoError(t, err)
    79  	require.Equal(t, 2, version)
    80  
    81  	value, err = store.Get("foo")
    82  	require.NoError(t, err)
    83  	verifyValue(t, value, "bar2", 2)
    84  }
    85  
    86  func TestNoCache(t *testing.T) {
    87  	ec, opts, closeFn := testStore(t)
    88  
    89  	store, err := NewStore(ec, opts)
    90  	require.NoError(t, err)
    91  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
    92  
    93  	version, err := store.Set("foo", genProto("bar1"))
    94  	require.NoError(t, err)
    95  	require.Equal(t, 1, version)
    96  
    97  	value, err := store.Get("foo")
    98  	require.NoError(t, err)
    99  	verifyValue(t, value, "bar1", 1)
   100  	// the will send a notification but won't trigger a sync
   101  	// because no cache file is set
   102  	require.Equal(t, 1, len(store.(*client).cacheUpdatedCh))
   103  
   104  	closeFn()
   105  
   106  	// from cache
   107  	value, err = store.Get("foo")
   108  	require.NoError(t, err)
   109  	verifyValue(t, value, "bar1", 1)
   110  
   111  	// new store but no cache file set
   112  	store, err = NewStore(ec, opts)
   113  	require.NoError(t, err)
   114  
   115  	_, err = store.Set("foo", genProto("bar1"))
   116  	require.Error(t, err)
   117  
   118  	_, err = store.Get("foo")
   119  	require.Error(t, err)
   120  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   121  }
   122  
   123  func TestCacheDirCreation(t *testing.T) {
   124  	ec, opts, closeFn := testStore(t)
   125  	defer closeFn()
   126  
   127  	tdir, err := ioutil.TempDir("", "m3tests")
   128  	require.NoError(t, err)
   129  	defer os.RemoveAll(tdir)
   130  
   131  	cdir := path.Join(tdir, "testCache")
   132  	opts = opts.SetCacheFileFn(func(string) string {
   133  		return path.Join(cdir, opts.Prefix())
   134  	})
   135  
   136  	store, err := NewStore(ec, opts)
   137  	require.NoError(t, err)
   138  
   139  	info, err := os.Stat(cdir)
   140  	require.NoError(t, err)
   141  	require.Equal(t, info.IsDir(), true)
   142  
   143  	_, err = store.Set("foo", genProto("bar"))
   144  	require.NoError(t, err)
   145  
   146  	value, err := store.Get("foo")
   147  	require.NoError(t, err)
   148  	verifyValue(t, value, "bar", 1)
   149  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   150  }
   151  
   152  func TestCache(t *testing.T) {
   153  	ec, opts, closeFn := testStore(t)
   154  
   155  	f, err := ioutil.TempFile("", "")
   156  	require.NoError(t, err)
   157  
   158  	opts = opts.SetCacheFileFn(func(string) string {
   159  		return f.Name()
   160  	})
   161  
   162  	store, err := NewStore(ec, opts)
   163  	require.NoError(t, err)
   164  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   165  
   166  	version, err := store.Set("foo", genProto("bar1"))
   167  	require.NoError(t, err)
   168  	require.Equal(t, 1, version)
   169  
   170  	value, err := store.Get("foo")
   171  	require.NoError(t, err)
   172  	verifyValue(t, value, "bar1", 1)
   173  	for {
   174  		// the notification should be picked up and trigger a sync
   175  		if len(store.(*client).cacheUpdatedCh) == 0 {
   176  			break
   177  		}
   178  	}
   179  	closeFn()
   180  
   181  	// from cache
   182  	value, err = store.Get("foo")
   183  	require.NoError(t, err)
   184  	verifyValue(t, value, "bar1", 1)
   185  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   186  
   187  	// new store but with cache file
   188  	store, err = NewStore(ec, opts)
   189  	require.NoError(t, err)
   190  
   191  	_, err = store.Set("key", genProto("bar1"))
   192  	require.Error(t, err)
   193  
   194  	_, err = store.Get("key")
   195  	require.Error(t, err)
   196  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   197  
   198  	value, err = store.Get("foo")
   199  	require.NoError(t, err)
   200  	verifyValue(t, value, "bar1", 1)
   201  	require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
   202  }
   203  
   204  func TestSetIfNotExist(t *testing.T) {
   205  	ec, opts, closeFn := testStore(t)
   206  	defer closeFn()
   207  
   208  	store, err := NewStore(ec, opts)
   209  	require.NoError(t, err)
   210  
   211  	version, err := store.SetIfNotExists("foo", genProto("bar"))
   212  	require.NoError(t, err)
   213  	require.Equal(t, 1, version)
   214  
   215  	_, err = store.SetIfNotExists("foo", genProto("bar"))
   216  	require.Equal(t, kv.ErrAlreadyExists, err)
   217  
   218  	value, err := store.Get("foo")
   219  	require.NoError(t, err)
   220  	verifyValue(t, value, "bar", 1)
   221  }
   222  
   223  func TestCheckAndSet(t *testing.T) {
   224  	ec, opts, closeFn := testStore(t)
   225  	defer closeFn()
   226  
   227  	store, err := NewStore(ec, opts)
   228  	require.NoError(t, err)
   229  
   230  	_, err = store.CheckAndSet("foo", 1, genProto("bar"))
   231  	require.Equal(t, kv.ErrVersionMismatch, err)
   232  
   233  	version, err := store.CheckAndSet("foo", 0, genProto("bar"))
   234  	require.NoError(t, err)
   235  	require.Equal(t, 1, version)
   236  
   237  	version, err = store.CheckAndSet("foo", 1, genProto("bar"))
   238  	require.NoError(t, err)
   239  	require.Equal(t, 2, version)
   240  
   241  	_, err = store.CheckAndSet("foo", 1, genProto("bar"))
   242  	require.Equal(t, kv.ErrVersionMismatch, err)
   243  
   244  	value, err := store.Get("foo")
   245  	require.NoError(t, err)
   246  	verifyValue(t, value, "bar", 2)
   247  }
   248  
   249  func TestWatchClose(t *testing.T) {
   250  	ec, opts, closeFn := testStore(t)
   251  	defer closeFn()
   252  
   253  	store, err := NewStore(ec, opts)
   254  	require.NoError(t, err)
   255  
   256  	_, err = store.Set("foo", genProto("bar1"))
   257  	require.NoError(t, err)
   258  	w1, err := store.Watch("foo")
   259  	require.NoError(t, err)
   260  	<-w1.C()
   261  	verifyValue(t, w1.Get(), "bar1", 1)
   262  
   263  	c := store.(*client)
   264  	_, ok := c.watchables["test/foo"]
   265  	require.True(t, ok)
   266  
   267  	// closing w1 will close the go routine for the watch updates
   268  	w1.Close()
   269  
   270  	// waits until the original watchable is cleaned up
   271  	for {
   272  		c.RLock()
   273  		_, ok = c.watchables["test/foo"]
   274  		c.RUnlock()
   275  		if !ok {
   276  			break
   277  		}
   278  		time.Sleep(1 * time.Millisecond)
   279  	}
   280  
   281  	// getting a new watch will create a new watchale and thread to watch for updates
   282  	w2, err := store.Watch("foo")
   283  	require.NoError(t, err)
   284  	<-w2.C()
   285  	verifyValue(t, w2.Get(), "bar1", 1)
   286  
   287  	// verify that w1 will no longer be updated because the original watchable is closed
   288  	_, err = store.Set("foo", genProto("bar2"))
   289  	require.NoError(t, err)
   290  	<-w2.C()
   291  	verifyValue(t, w2.Get(), "bar2", 2)
   292  	verifyValue(t, w1.Get(), "bar1", 1)
   293  
   294  	w1.Close()
   295  	w2.Close()
   296  }
   297  
   298  func TestWatchLastVersion(t *testing.T) {
   299  	ec, opts, closeFn := testStore(t)
   300  	defer closeFn()
   301  
   302  	store, err := NewStore(ec, opts)
   303  	require.NoError(t, err)
   304  
   305  	w, err := store.Watch("foo")
   306  	require.NoError(t, err)
   307  	require.Nil(t, w.Get())
   308  
   309  	var (
   310  		doneCh      = make(chan struct{})
   311  		lastVersion = 50
   312  	)
   313  
   314  	go func() {
   315  		for i := 1; i <= lastVersion; i++ {
   316  			_, err := store.Set("foo", genProto(fmt.Sprintf("bar%d", i)))
   317  			assert.NoError(t, err)
   318  		}
   319  	}()
   320  
   321  	go func() {
   322  		defer close(doneCh)
   323  		for {
   324  			<-w.C()
   325  			value := w.Get()
   326  			if value.Version() == lastVersion {
   327  				return
   328  			}
   329  		}
   330  	}()
   331  
   332  	select {
   333  	case <-time.After(5 * time.Second):
   334  		t.Fatal("test timed out")
   335  	case <-doneCh:
   336  	}
   337  	verifyValue(t, w.Get(), fmt.Sprintf("bar%d", lastVersion), lastVersion)
   338  }
   339  
   340  func TestWatchFromExist(t *testing.T) {
   341  	ec, opts, closeFn := testStore(t)
   342  	defer closeFn()
   343  
   344  	store, err := NewStore(ec, opts)
   345  	require.NoError(t, err)
   346  
   347  	_, err = store.Set("foo", genProto("bar1"))
   348  	require.NoError(t, err)
   349  	value, err := store.Get("foo")
   350  	require.NoError(t, err)
   351  	verifyValue(t, value, "bar1", 1)
   352  
   353  	w, err := store.Watch("foo")
   354  	require.NoError(t, err)
   355  
   356  	<-w.C()
   357  	require.Equal(t, 0, len(w.C()))
   358  	verifyValue(t, w.Get(), "bar1", 1)
   359  
   360  	_, err = store.Set("foo", genProto("bar2"))
   361  	require.NoError(t, err)
   362  
   363  	<-w.C()
   364  	require.Equal(t, 0, len(w.C()))
   365  	verifyValue(t, w.Get(), "bar2", 2)
   366  
   367  	_, err = store.Set("foo", genProto("bar3"))
   368  	require.NoError(t, err)
   369  
   370  	<-w.C()
   371  	require.Equal(t, 0, len(w.C()))
   372  	verifyValue(t, w.Get(), "bar3", 3)
   373  
   374  	w.Close()
   375  }
   376  
   377  func TestWatchFromNotExist(t *testing.T) {
   378  	ec, opts, closeFn := testStore(t)
   379  	defer closeFn()
   380  
   381  	store, err := NewStore(ec, opts)
   382  	require.NoError(t, err)
   383  
   384  	w, err := store.Watch("foo")
   385  	require.NoError(t, err)
   386  	require.Equal(t, 0, len(w.C()))
   387  	require.Nil(t, w.Get())
   388  
   389  	_, err = store.Set("foo", genProto("bar1"))
   390  	require.NoError(t, err)
   391  
   392  	<-w.C()
   393  	require.Equal(t, 0, len(w.C()))
   394  	verifyValue(t, w.Get(), "bar1", 1)
   395  
   396  	_, err = store.Set("foo", genProto("bar2"))
   397  	require.NoError(t, err)
   398  
   399  	<-w.C()
   400  	require.Equal(t, 0, len(w.C()))
   401  	verifyValue(t, w.Get(), "bar2", 2)
   402  
   403  	w.Close()
   404  }
   405  
   406  func TestGetFromKvNotFound(t *testing.T) {
   407  	ec, opts, closeFn := testStore(t)
   408  	defer closeFn()
   409  	store, err := NewStore(ec, opts)
   410  	require.NoError(t, err)
   411  	c := store.(*client)
   412  	_, err = c.Set("foo", genProto("bar1"))
   413  	require.NoError(t, err)
   414  
   415  	val, err := c.getFromKVStore("foo2")
   416  	require.NoError(t, err)
   417  	require.Nil(t, val)
   418  }
   419  
   420  func TestMultipleWatchesFromExist(t *testing.T) {
   421  	ec, opts, closeFn := testStore(t)
   422  	defer closeFn()
   423  
   424  	store, err := NewStore(ec, opts)
   425  	require.NoError(t, err)
   426  
   427  	_, err = store.Set("foo", genProto("bar1"))
   428  	require.NoError(t, err)
   429  
   430  	w1, err := store.Watch("foo")
   431  	require.NoError(t, err)
   432  
   433  	w2, err := store.Watch("foo")
   434  	require.NoError(t, err)
   435  
   436  	<-w1.C()
   437  	require.Equal(t, 0, len(w1.C()))
   438  	verifyValue(t, w1.Get(), "bar1", 1)
   439  
   440  	<-w2.C()
   441  	require.Equal(t, 0, len(w2.C()))
   442  	verifyValue(t, w2.Get(), "bar1", 1)
   443  
   444  	_, err = store.Set("foo", genProto("bar2"))
   445  	require.NoError(t, err)
   446  
   447  	<-w1.C()
   448  	require.Equal(t, 0, len(w1.C()))
   449  	verifyValue(t, w1.Get(), "bar2", 2)
   450  
   451  	<-w2.C()
   452  	require.Equal(t, 0, len(w2.C()))
   453  	verifyValue(t, w2.Get(), "bar2", 2)
   454  
   455  	_, err = store.Set("foo", genProto("bar3"))
   456  	require.NoError(t, err)
   457  
   458  	<-w1.C()
   459  	require.Equal(t, 0, len(w1.C()))
   460  	verifyValue(t, w1.Get(), "bar3", 3)
   461  
   462  	<-w2.C()
   463  	require.Equal(t, 0, len(w2.C()))
   464  	verifyValue(t, w2.Get(), "bar3", 3)
   465  
   466  	w1.Close()
   467  	w2.Close()
   468  }
   469  
   470  func TestMultipleWatchesFromNotExist(t *testing.T) {
   471  	ec, opts, closeFn := testStore(t)
   472  	defer closeFn()
   473  
   474  	store, err := NewStore(ec, opts)
   475  	require.NoError(t, err)
   476  	w1, err := store.Watch("foo")
   477  	require.NoError(t, err)
   478  	require.Equal(t, 0, len(w1.C()))
   479  	require.Nil(t, w1.Get())
   480  
   481  	w2, err := store.Watch("foo")
   482  	require.NoError(t, err)
   483  	require.Equal(t, 0, len(w2.C()))
   484  	require.Nil(t, w2.Get())
   485  
   486  	_, err = store.Set("foo", genProto("bar1"))
   487  	require.NoError(t, err)
   488  
   489  	<-w1.C()
   490  	require.Equal(t, 0, len(w1.C()))
   491  	verifyValue(t, w1.Get(), "bar1", 1)
   492  
   493  	<-w2.C()
   494  	require.Equal(t, 0, len(w2.C()))
   495  	verifyValue(t, w2.Get(), "bar1", 1)
   496  
   497  	_, err = store.Set("foo", genProto("bar2"))
   498  	require.NoError(t, err)
   499  
   500  	<-w1.C()
   501  	require.Equal(t, 0, len(w1.C()))
   502  	verifyValue(t, w1.Get(), "bar2", 2)
   503  
   504  	<-w2.C()
   505  	require.Equal(t, 0, len(w2.C()))
   506  	verifyValue(t, w2.Get(), "bar2", 2)
   507  
   508  	w1.Close()
   509  	w2.Close()
   510  }
   511  
   512  func TestWatchNonBlocking(t *testing.T) {
   513  	ecluster, opts, closeFn := testCluster(t)
   514  	defer closeFn()
   515  
   516  	ec := ecluster.Client(0)
   517  
   518  	opts = opts.SetWatchChanResetInterval(200 * time.Millisecond).SetWatchChanInitTimeout(500 * time.Millisecond)
   519  
   520  	store, err := NewStore(ec, opts)
   521  	require.NoError(t, err)
   522  	c := store.(*client)
   523  
   524  	_, err = c.Set("foo", genProto("bar1"))
   525  	require.NoError(t, err)
   526  
   527  	before := time.Now()
   528  	ecluster.Members[0].Bridge().Blackhole()
   529  	w1, err := c.Watch("foo")
   530  	require.WithinDuration(t, time.Now(), before, 100*time.Millisecond)
   531  	require.NoError(t, err)
   532  	require.Equal(t, 0, len(w1.C()))
   533  	ecluster.Members[0].Bridge().Unblackhole()
   534  	ecluster.Members[0].Bridge().DropConnections()
   535  
   536  	// watch channel will error out, but Get() will be tried
   537  	<-w1.C()
   538  	verifyValue(t, w1.Get(), "bar1", 1)
   539  
   540  	w1.Close()
   541  }
   542  
   543  func TestHistory(t *testing.T) {
   544  	ec, opts, closeFn := testStore(t)
   545  	defer closeFn()
   546  
   547  	store, err := NewStore(ec, opts)
   548  	require.NoError(t, err)
   549  
   550  	_, err = store.History("k1", 10, 5)
   551  	require.Error(t, err)
   552  
   553  	_, err = store.History("k1", 0, 5)
   554  	require.Error(t, err)
   555  
   556  	_, err = store.History("k1", -5, 0)
   557  	require.Error(t, err)
   558  
   559  	totalVersion := 10
   560  	for i := 1; i <= totalVersion; i++ {
   561  		store.Set("k1", genProto(fmt.Sprintf("bar%d", i)))
   562  		store.Set("k2", genProto(fmt.Sprintf("bar%d", i)))
   563  	}
   564  
   565  	res, err := store.History("k1", 5, 5)
   566  	require.NoError(t, err)
   567  	require.Equal(t, 0, len(res))
   568  
   569  	res, err = store.History("k1", 15, 20)
   570  	require.NoError(t, err)
   571  	require.Equal(t, 0, len(res))
   572  
   573  	res, err = store.History("k1", 6, 10)
   574  	require.NoError(t, err)
   575  	require.Equal(t, 4, len(res))
   576  	for i := 0; i < len(res); i++ {
   577  		version := i + 6
   578  		value := res[i]
   579  		verifyValue(t, value, fmt.Sprintf("bar%d", version), version)
   580  	}
   581  
   582  	res, err = store.History("k1", 3, 7)
   583  	require.NoError(t, err)
   584  	require.Equal(t, 4, len(res))
   585  	for i := 0; i < len(res); i++ {
   586  		version := i + 3
   587  		value := res[i]
   588  		verifyValue(t, value, fmt.Sprintf("bar%d", version), version)
   589  	}
   590  
   591  	res, err = store.History("k1", 5, 15)
   592  	require.NoError(t, err)
   593  	require.Equal(t, totalVersion-5+1, len(res))
   594  	for i := 0; i < len(res); i++ {
   595  		version := i + 5
   596  		value := res[i]
   597  		verifyValue(t, value, fmt.Sprintf("bar%d", version), version)
   598  	}
   599  }
   600  
   601  func TestDelete(t *testing.T) {
   602  	ec, opts, closeFn := testStore(t)
   603  	defer closeFn()
   604  
   605  	store, err := NewStore(ec, opts)
   606  	require.NoError(t, err)
   607  
   608  	_, err = store.Delete("foo")
   609  	require.Equal(t, kv.ErrNotFound, err)
   610  
   611  	version, err := store.Set("foo", genProto("bar1"))
   612  	require.NoError(t, err)
   613  	require.Equal(t, 1, version)
   614  
   615  	version, err = store.Set("foo", genProto("bar2"))
   616  	require.NoError(t, err)
   617  	require.Equal(t, 2, version)
   618  
   619  	v, err := store.Get("foo")
   620  	require.NoError(t, err)
   621  	verifyValue(t, v, "bar2", 2)
   622  
   623  	v, err = store.Delete("foo")
   624  	require.NoError(t, err)
   625  	verifyValue(t, v, "bar2", 2)
   626  
   627  	_, err = store.Delete("foo")
   628  	require.Equal(t, kv.ErrNotFound, err)
   629  
   630  	_, err = store.Get("foo")
   631  	require.Equal(t, kv.ErrNotFound, err)
   632  
   633  	version, err = store.SetIfNotExists("foo", genProto("bar1"))
   634  	require.NoError(t, err)
   635  	require.Equal(t, 1, version)
   636  }
   637  
   638  func TestDelete_UpdateCache(t *testing.T) {
   639  	ec, opts, closeFn := testStore(t)
   640  	defer closeFn()
   641  
   642  	c, err := NewStore(ec, opts)
   643  	require.NoError(t, err)
   644  
   645  	store := c.(*client)
   646  	version, err := store.Set("foo", genProto("bar1"))
   647  	require.NoError(t, err)
   648  	require.Equal(t, 1, version)
   649  
   650  	v, err := store.Get("foo")
   651  	require.NoError(t, err)
   652  	verifyValue(t, v, "bar1", 1)
   653  	require.Equal(t, 1, len(store.cache.Values))
   654  	require.Equal(t, 1, len(store.cacheUpdatedCh))
   655  
   656  	// drain the notification
   657  	<-store.cacheUpdatedCh
   658  
   659  	v, err = store.Delete("foo")
   660  	require.NoError(t, err)
   661  	verifyValue(t, v, "bar1", 1)
   662  	// make sure the cache is cleaned up
   663  	require.Equal(t, 0, len(store.cache.Values))
   664  	require.Equal(t, 1, len(store.cacheUpdatedCh))
   665  }
   666  
   667  func TestDelete_UpdateWatcherCache(t *testing.T) {
   668  	ec, opts, closeFn := testStore(t)
   669  	defer closeFn()
   670  
   671  	setStore, err := NewStore(ec, opts)
   672  	require.NoError(t, err)
   673  
   674  	setClient := setStore.(*client)
   675  	version, err := setClient.Set("foo", genProto("bar1"))
   676  	require.NoError(t, err)
   677  	require.Equal(t, 1, version)
   678  
   679  	setV, err := setClient.Get("foo")
   680  	require.NoError(t, err)
   681  	verifyValue(t, setV, "bar1", 1)
   682  	require.Equal(t, 1, len(setClient.cache.Values))
   683  	require.Equal(t, 1, len(setClient.cacheUpdatedCh))
   684  
   685  	// drain the notification to ensure set received update
   686  	<-setClient.cacheUpdatedCh
   687  
   688  	// make a custom cache path for the get client
   689  	clientCachePath, err := ioutil.TempDir("", "client-cache-dir")
   690  	require.NoError(t, err)
   691  	defer os.RemoveAll(clientCachePath)
   692  
   693  	getStore, err := NewStore(ec, opts.SetCacheFileFn(func(ns string) string {
   694  		nsFile := path.Join(clientCachePath, fmt.Sprintf("%s.json", ns))
   695  		return nsFile
   696  	}))
   697  	require.NoError(t, err)
   698  	getClient := getStore.(*client)
   699  
   700  	getW, err := getClient.Watch("foo")
   701  	require.NoError(t, err)
   702  	<-getW.C()
   703  	verifyValue(t, getW.Get(), "bar1", 1)
   704  	require.True(t, xclock.WaitUntil(func() bool {
   705  		getClient.cache.RLock()
   706  		defer getClient.cache.RUnlock()
   707  		return 1 == len(getClient.cache.Values)
   708  	}, time.Minute))
   709  
   710  	var (
   711  		originalBytes []byte
   712  	)
   713  	require.True(t, xclock.WaitUntil(func() bool {
   714  		originalBytes, err = readCacheJSON(clientCachePath)
   715  		return err == nil
   716  	}, time.Minute))
   717  
   718  	setV, err = setClient.Delete("foo")
   719  	require.NoError(t, err)
   720  	verifyValue(t, setV, "bar1", 1)
   721  
   722  	// make sure the cache is cleaned up on set client
   723  	require.Equal(t, 0, len(setClient.cache.Values))
   724  	require.Equal(t, 1, len(setClient.cacheUpdatedCh))
   725  
   726  	// make sure the cache is cleaned up on get client (mem and disk)
   727  	var (
   728  		updatedBytes []byte
   729  	)
   730  	require.True(t, xclock.WaitUntil(func() bool {
   731  		getClient.cache.RLock()
   732  		defer getClient.cache.RUnlock()
   733  		if len(getClient.cache.Values) != 0 {
   734  			return false
   735  		}
   736  		updatedBytes, err = readCacheJSON(clientCachePath)
   737  		if err != nil {
   738  			return false
   739  		}
   740  		return !bytes.Equal(updatedBytes, originalBytes)
   741  	}, time.Minute), "did not observe any invalidation of the cache file")
   742  }
   743  
   744  func TestDelete_TriggerWatch(t *testing.T) {
   745  	ec, opts, closeFn := testStore(t)
   746  	defer closeFn()
   747  
   748  	store, err := NewStore(ec, opts)
   749  	require.NoError(t, err)
   750  
   751  	vw, err := store.Watch("foo")
   752  	require.NoError(t, err)
   753  
   754  	_, err = store.Set("foo", genProto("bar1"))
   755  	require.NoError(t, err)
   756  
   757  	<-vw.C()
   758  	verifyValue(t, vw.Get(), "bar1", 1)
   759  
   760  	_, err = store.Set("foo", genProto("bar2"))
   761  	require.NoError(t, err)
   762  
   763  	<-vw.C()
   764  	verifyValue(t, vw.Get(), "bar2", 2)
   765  
   766  	_, err = store.Delete("foo")
   767  	require.NoError(t, err)
   768  
   769  	<-vw.C()
   770  	require.Nil(t, vw.Get())
   771  
   772  	_, err = store.Set("foo", genProto("bar3"))
   773  	require.NoError(t, err)
   774  
   775  	<-vw.C()
   776  	verifyValue(t, vw.Get(), "bar3", 1)
   777  }
   778  
   779  func TestStaleDelete__FromGet(t *testing.T) {
   780  	integration.BeforeTestExternal(t)
   781  	// in this test we ensure clients who did not receive a delete for a key in
   782  	// their caches, evict the value in their cache the next time they communicate
   783  	// with an etcd which is unaware of the key (e.g. it's been compacted).
   784  
   785  	// first, we find the bytes required to be created in the cache file
   786  	serverCachePath, err := ioutil.TempDir("", "server-cache-dir")
   787  	require.NoError(t, err)
   788  	defer os.RemoveAll(serverCachePath)
   789  	ec, opts, closeFn := testStore(t, func(tc *testStoreOpts) {
   790  		tc.etcdBeforeTestExternal = false
   791  	})
   792  
   793  	setStore, err := NewStore(ec, opts.SetCacheFileFn(func(ns string) string {
   794  		return path.Join(serverCachePath, fmt.Sprintf("%s.json", ns))
   795  	}))
   796  	require.NoError(t, err)
   797  
   798  	setClient := setStore.(*client)
   799  	version, err := setClient.Set("foo", genProto("bar1"))
   800  	require.NoError(t, err)
   801  	require.Equal(t, 1, version)
   802  
   803  	setV, err := setClient.Get("foo")
   804  	require.NoError(t, err)
   805  	verifyValue(t, setV, "bar1", 1)
   806  	require.Equal(t, 1, len(setClient.cache.Values))
   807  
   808  	// drain the notification to ensure set received update
   809  	var (
   810  		fileName   string
   811  		cacheBytes []byte
   812  	)
   813  	require.True(t, xclock.WaitUntil(func() bool {
   814  		fileName, cacheBytes, err = readCacheJSONAndFilename(serverCachePath)
   815  		return err == nil && isValidJSON(cacheBytes)
   816  	}, time.Minute), "timed out waiting to read cache file")
   817  	closeFn()
   818  
   819  	// make a new etcd cluster (to mimic the case where etcd has deleted and compacted
   820  	// the value).
   821  	newServerCachePath, err := ioutil.TempDir("", "new-server-cache-dir")
   822  	require.NoError(t, err)
   823  	defer os.RemoveAll(newServerCachePath)
   824  
   825  	// write the cache file with correct contents in the new location
   826  	f, err := os.Create(path.Join(newServerCachePath, fileName))
   827  	require.NoError(t, err)
   828  	_, err = f.Write(cacheBytes)
   829  	require.NoError(t, err)
   830  	require.NoError(t, f.Sync())
   831  	require.NoError(t, f.Close())
   832  
   833  	require.True(t, xclock.WaitUntil(func() bool {
   834  		_, newBytes, err := readCacheJSONAndFilename(newServerCachePath)
   835  		return err == nil && bytes.Equal(cacheBytes, newBytes)
   836  	}, time.Minute), "timed out waiting to flush new cache file")
   837  
   838  	// create new etcd cluster
   839  	ec2, opts, closeFn2 := testStore(t, withNoEtcdBeforeTestExternal())
   840  	defer closeFn2()
   841  	getStore, err := NewStore(ec2, opts.SetCacheFileFn(func(ns string) string {
   842  		nsFile := path.Join(newServerCachePath, fmt.Sprintf("%s.json", ns))
   843  		return nsFile
   844  	}))
   845  	require.NoError(t, err)
   846  	getClient := getStore.(*client)
   847  
   848  	require.True(t, xclock.WaitUntil(func() bool {
   849  		getClient.cache.RLock()
   850  		defer getClient.cache.RUnlock()
   851  		return 1 == len(getClient.cache.Values)
   852  	}, time.Minute), "timed out waiting for client to read values from cache")
   853  
   854  	// get value and ensure it's not able to serve the read
   855  	v, err := getClient.Get("foo")
   856  	require.Error(t, kv.ErrNotFound)
   857  	require.Nil(t, v)
   858  
   859  	require.True(t, xclock.WaitUntil(func() bool {
   860  		_, updatedBytes, err := readCacheJSONAndFilename(newServerCachePath)
   861  		return err == nil && !bytes.Equal(cacheBytes, updatedBytes)
   862  	}, time.Minute), "timed out waiting to flush cache file delete")
   863  }
   864  
   865  func TestStaleDelete__FromWatch(t *testing.T) {
   866  	integration.BeforeTestExternal(t)
   867  	// in this test we ensure clients who did not receive a delete for a key in
   868  	// their caches, evict the value in their cache the next time they communicate
   869  	// with an etcd which is unaware of the key (e.g. it's been compacted).
   870  	// first, we find the bytes required to be created in the cache file
   871  	serverCachePath, err := ioutil.TempDir("", "server-cache-dir")
   872  	require.NoError(t, err)
   873  	defer os.RemoveAll(serverCachePath)
   874  	ec, opts, closeFn := testStore(t, withNoEtcdBeforeTestExternal())
   875  
   876  	setStore, err := NewStore(ec, opts.SetCacheFileFn(func(ns string) string {
   877  		return path.Join(serverCachePath, fmt.Sprintf("%s.json", ns))
   878  	}))
   879  	require.NoError(t, err)
   880  
   881  	setClient := setStore.(*client)
   882  	version, err := setClient.Set("foo", genProto("bar1"))
   883  	require.NoError(t, err)
   884  	require.Equal(t, 1, version)
   885  
   886  	setV, err := setClient.Get("foo")
   887  	require.NoError(t, err)
   888  	verifyValue(t, setV, "bar1", 1)
   889  	require.Equal(t, 1, len(setClient.cache.Values))
   890  
   891  	// drain the notification to ensure set received update
   892  	var (
   893  		fileName   string
   894  		cacheBytes []byte
   895  	)
   896  	require.True(t, xclock.WaitUntil(func() bool {
   897  		fileName, cacheBytes, err = readCacheJSONAndFilename(serverCachePath)
   898  		// Need to make sure it is valid JSON to ensure we're not reading the
   899  		// bytes before they've been completely written out.
   900  		return err == nil && isValidJSON(cacheBytes)
   901  	}, time.Minute), "timed out waiting to read cache file")
   902  	closeFn()
   903  
   904  	// make a new etcd cluster (to mimic the case where etcd has deleted and compacted
   905  	// the value).
   906  	newServerCachePath, err := ioutil.TempDir("", "new-server-cache-dir")
   907  	require.NoError(t, err)
   908  	defer os.RemoveAll(newServerCachePath)
   909  
   910  	// write the cache file with correct contents in the new location
   911  	f, err := os.Create(path.Join(newServerCachePath, fileName))
   912  	require.NoError(t, err)
   913  	_, err = f.Write(cacheBytes)
   914  	require.NoError(t, err)
   915  	require.NoError(t, f.Sync())
   916  	require.NoError(t, f.Close())
   917  
   918  	require.True(t, xclock.WaitUntil(func() bool {
   919  		_, newBytes, err := readCacheJSONAndFilename(newServerCachePath)
   920  		return err == nil && bytes.Equal(cacheBytes, newBytes) && isValidJSON(newBytes)
   921  	}, time.Minute), "timed out waiting to flush new cache file")
   922  
   923  	// create new etcd cluster
   924  	ec2, opts, closeFn2 := testStore(t, withNoEtcdBeforeTestExternal())
   925  	defer closeFn2()
   926  	getStore, err := NewStore(ec2, opts.SetCacheFileFn(func(ns string) string {
   927  		nsFile := path.Join(newServerCachePath, fmt.Sprintf("%s.json", ns))
   928  		return nsFile
   929  	}))
   930  	require.NoError(t, err)
   931  	getClient := getStore.(*client)
   932  
   933  	require.True(t, xclock.WaitUntil(func() bool {
   934  		getClient.cache.RLock()
   935  		defer getClient.cache.RUnlock()
   936  		return 1 == len(getClient.cache.Values)
   937  	}, time.Minute), "timed out waiting for client to read values from cache")
   938  	require.Equal(t, 1, len(getClient.cache.Values))
   939  
   940  	// get value and ensure it's not able to serve the read
   941  	w, err := getClient.Watch("foo")
   942  	require.NoError(t, err)
   943  	require.NotNil(t, w)
   944  	require.Nil(t, w.Get())
   945  
   946  	require.True(t, xclock.WaitUntil(func() bool {
   947  		_, updatedBytes, err := readCacheJSONAndFilename(newServerCachePath)
   948  		return err == nil && !bytes.Equal(cacheBytes, updatedBytes)
   949  	}, time.Minute), "timed out waiting to flush cache file delete")
   950  	require.Equal(t, 0, len(getClient.cache.Values))
   951  	require.Nil(t, w.Get())
   952  }
   953  
   954  func isValidJSON(b []byte) bool {
   955  	var j interface{}
   956  	return json.Unmarshal(b, &j) == nil
   957  }
   958  
   959  func TestTxn(t *testing.T) {
   960  	ec, opts, closeFn := testStore(t)
   961  	defer closeFn()
   962  
   963  	store, err := NewStore(ec, opts)
   964  	require.NoError(t, err)
   965  
   966  	r, err := store.Commit(
   967  		[]kv.Condition{
   968  			kv.NewCondition().
   969  				SetCompareType(kv.CompareEqual).
   970  				SetTargetType(kv.TargetVersion).
   971  				SetKey("foo").
   972  				SetValue(0),
   973  		},
   974  		[]kv.Op{kv.NewSetOp("foo", genProto("bar1"))},
   975  	)
   976  	require.NoError(t, err)
   977  	require.Equal(t, 1, len(r.Responses()))
   978  	require.Equal(t, "foo", r.Responses()[0].Key())
   979  	require.Equal(t, kv.OpSet, r.Responses()[0].Type())
   980  	require.Equal(t, 1, r.Responses()[0].Value())
   981  
   982  	v, err := store.Set("key", genProto("bar1"))
   983  	require.NoError(t, err)
   984  	require.Equal(t, 1, v)
   985  
   986  	r, err = store.Commit(
   987  		[]kv.Condition{
   988  			kv.NewCondition().
   989  				SetCompareType(kv.CompareEqual).
   990  				SetTargetType(kv.TargetVersion).
   991  				SetKey("key").
   992  				SetValue(1),
   993  		},
   994  		[]kv.Op{kv.NewSetOp("foo", genProto("bar1"))},
   995  	)
   996  	require.NoError(t, err)
   997  	require.Equal(t, 1, len(r.Responses()))
   998  	require.Equal(t, "foo", r.Responses()[0].Key())
   999  	require.Equal(t, kv.OpSet, r.Responses()[0].Type())
  1000  	require.Equal(t, 2, r.Responses()[0].Value())
  1001  
  1002  	r, err = store.Commit(
  1003  		[]kv.Condition{
  1004  			kv.NewCondition().
  1005  				SetCompareType(kv.CompareEqual).
  1006  				SetTargetType(kv.TargetVersion).
  1007  				SetKey("foo").
  1008  				SetValue(2),
  1009  			kv.NewCondition().
  1010  				SetCompareType(kv.CompareEqual).
  1011  				SetTargetType(kv.TargetVersion).
  1012  				SetKey("key").
  1013  				SetValue(1),
  1014  		},
  1015  		[]kv.Op{
  1016  			kv.NewSetOp("key", genProto("bar1")),
  1017  			kv.NewSetOp("foo", genProto("bar2")),
  1018  		},
  1019  	)
  1020  	require.NoError(t, err)
  1021  	require.Equal(t, 2, len(r.Responses()))
  1022  	require.Equal(t, "key", r.Responses()[0].Key())
  1023  	require.Equal(t, kv.OpSet, r.Responses()[0].Type())
  1024  	require.Equal(t, 2, r.Responses()[0].Value())
  1025  	require.Equal(t, "foo", r.Responses()[1].Key())
  1026  	require.Equal(t, kv.OpSet, r.Responses()[1].Type())
  1027  	require.Equal(t, 3, r.Responses()[1].Value())
  1028  }
  1029  
  1030  func TestTxn_ConditionFail(t *testing.T) {
  1031  	ec, opts, closeFn := testStore(t)
  1032  	defer closeFn()
  1033  
  1034  	store, err := NewStore(ec, opts)
  1035  	require.NoError(t, err)
  1036  
  1037  	_, err = store.Commit(
  1038  		[]kv.Condition{
  1039  			kv.NewCondition().
  1040  				SetCompareType(kv.CompareEqual).
  1041  				SetTargetType(kv.TargetVersion).
  1042  				SetKey("foo").
  1043  				SetValue(1),
  1044  		},
  1045  		[]kv.Op{kv.NewSetOp("foo", genProto("bar1"))},
  1046  	)
  1047  	require.Error(t, err)
  1048  
  1049  	store.Set("key1", genProto("v1"))
  1050  	store.Set("key2", genProto("v2"))
  1051  	_, err = store.Commit(
  1052  		[]kv.Condition{
  1053  			kv.NewCondition().
  1054  				SetCompareType(kv.CompareEqual).
  1055  				SetTargetType(kv.TargetVersion).
  1056  				SetKey("key1").
  1057  				SetValue(1),
  1058  			kv.NewCondition().
  1059  				SetCompareType(kv.CompareEqual).
  1060  				SetTargetType(kv.TargetVersion).
  1061  				SetKey("key2").
  1062  				SetValue(2),
  1063  		},
  1064  		[]kv.Op{kv.NewSetOp("foo", genProto("bar1"))},
  1065  	)
  1066  	require.Error(t, err)
  1067  }
  1068  
  1069  func TestTxn_UnknownType(t *testing.T) {
  1070  	ec, opts, closeFn := testStore(t)
  1071  	defer closeFn()
  1072  
  1073  	store, err := NewStore(ec, opts)
  1074  	require.NoError(t, err)
  1075  
  1076  	_, err = store.Commit(
  1077  		[]kv.Condition{
  1078  			kv.NewCondition().
  1079  				SetTargetType(kv.TargetVersion).
  1080  				SetKey("foo").
  1081  				SetValue(1),
  1082  		},
  1083  		[]kv.Op{kv.NewSetOp("foo", genProto("bar1"))},
  1084  	)
  1085  	require.Equal(t, kv.ErrUnknownCompareType, err)
  1086  }
  1087  
  1088  // TestWatchWithStartRevision that watching from 1) an old compacted start
  1089  // revision and 2) a start revision in the future are both safe
  1090  func TestWatchWithStartRevision(t *testing.T) {
  1091  	tests := map[string]int64{
  1092  		"old_revision":    1,
  1093  		"future_revision": 100000,
  1094  	}
  1095  
  1096  	for name, rev := range tests {
  1097  		t.Run(name, func(t *testing.T) {
  1098  			ec, opts, closeFn := testStore(t)
  1099  			defer closeFn()
  1100  
  1101  			opts = opts.SetWatchWithRevision(rev)
  1102  
  1103  			store, err := NewStore(ec, opts)
  1104  			require.NoError(t, err)
  1105  
  1106  			for i := 1; i <= 50; i++ {
  1107  				_, err = store.Set("foo", genProto(fmt.Sprintf("bar-%d", i)))
  1108  				require.NoError(t, err)
  1109  			}
  1110  
  1111  			cl := store.(*client).kv
  1112  
  1113  			resp, err := cl.Get(context.Background(), "foo")
  1114  			require.NoError(t, err)
  1115  			compactRev := resp.Header.Revision
  1116  
  1117  			_, err = cl.Compact(context.Background(), compactRev-1)
  1118  			require.NoError(t, err)
  1119  
  1120  			w1, err := store.Watch("foo")
  1121  			require.NoError(t, err)
  1122  			<-w1.C()
  1123  			verifyValue(t, w1.Get(), "bar-50", 50)
  1124  		})
  1125  	}
  1126  }
  1127  
  1128  func TestSerializedGets(t *testing.T) {
  1129  	ec, opts, closeFn := testStore(t)
  1130  	defer closeFn()
  1131  
  1132  	opts = opts.SetEnableFastGets(true)
  1133  	require.NoError(t, opts.Validate())
  1134  
  1135  	store, err := NewStore(ec, opts)
  1136  	require.NoError(t, err)
  1137  
  1138  	v, err := store.Set("foo", genProto("bar"))
  1139  	require.EqualValues(t, 1, v)
  1140  	require.NoError(t, err)
  1141  
  1142  	val, err := store.Get("foo")
  1143  	verifyValue(t, val, "bar", 1)
  1144  	require.NoError(t, err)
  1145  
  1146  	v, err = store.Set("foo", genProto("42"))
  1147  	require.EqualValues(t, 2, v)
  1148  	require.NoError(t, err)
  1149  
  1150  	val, err = store.Get("foo")
  1151  	verifyValue(t, val, "42", 2)
  1152  	require.NoError(t, err)
  1153  }
  1154  
  1155  func verifyValue(t *testing.T, v kv.Value, value string, version int) {
  1156  	var testMsg kvtest.Foo
  1157  	err := v.Unmarshal(&testMsg)
  1158  	require.NoError(t, err)
  1159  	require.Equal(t, value, testMsg.Msg)
  1160  	require.Equal(t, version, v.Version())
  1161  }
  1162  
  1163  func genProto(msg string) proto.Message {
  1164  	return &kvtest.Foo{Msg: msg}
  1165  }
  1166  
  1167  func testCluster(t *testing.T, testOpts ...testStoreOption) (*integration.Cluster, Options, func()) {
  1168  	cfg := testStoreOpts{etcdBeforeTestExternal: true}
  1169  	for _, opt := range testOpts {
  1170  		opt(&cfg)
  1171  	}
  1172  
  1173  	if cfg.etcdBeforeTestExternal {
  1174  		integration.BeforeTestExternal(t)
  1175  	}
  1176  
  1177  	ecluster := integration.NewCluster(t, &integration.ClusterConfig{
  1178  		Size:      1,
  1179  		UseBridge: true,
  1180  	})
  1181  	closer := func() {
  1182  		ecluster.Terminate(t)
  1183  	}
  1184  
  1185  	opts := NewOptions().
  1186  		SetWatchChanCheckInterval(100 * time.Millisecond).
  1187  		SetWatchChanResetInterval(200 * time.Millisecond).
  1188  		SetWatchChanInitTimeout(200 * time.Millisecond).
  1189  		SetRequestTimeout(200 * time.Millisecond).
  1190  		SetRetryOptions(retry.NewOptions().SetMaxRetries(1).SetMaxBackoff(0)).
  1191  		SetPrefix("test")
  1192  
  1193  	return ecluster, opts, closer
  1194  }
  1195  
  1196  type testStoreOpts struct {
  1197  	etcdBeforeTestExternal bool
  1198  }
  1199  
  1200  type testStoreOption func(tc *testStoreOpts)
  1201  
  1202  func withNoEtcdBeforeTestExternal() testStoreOption {
  1203  	return func(tc *testStoreOpts) {
  1204  		tc.etcdBeforeTestExternal = false
  1205  	}
  1206  }
  1207  
  1208  func testStore(t *testing.T, testOpts ...testStoreOption) (*clientv3.Client, Options, func()) {
  1209  	ecluster, opts, closer := testCluster(t, testOpts...)
  1210  	return ecluster.RandClient(), opts, closer
  1211  }
  1212  
  1213  func readCacheJSONAndFilename(dirPath string) (string, []byte, error) {
  1214  	files, err := ioutil.ReadDir(dirPath)
  1215  	if err != nil {
  1216  		return "", nil, err
  1217  	}
  1218  	if len(files) != 1 {
  1219  		return "", nil, fmt.Errorf("expected 1 file, found files: %+v", files)
  1220  	}
  1221  	fileName := files[0].Name()
  1222  	filepath := path.Join(dirPath, fileName)
  1223  	f, err := os.Open(filepath)
  1224  	if err != nil {
  1225  		return "", nil, err
  1226  	}
  1227  	b, err := ioutil.ReadAll(f)
  1228  	if err != nil {
  1229  		return "", nil, err
  1230  	}
  1231  	return fileName, b, nil
  1232  }
  1233  
  1234  func readCacheJSON(dirPath string) ([]byte, error) {
  1235  	_, b, err := readCacheJSONAndFilename(dirPath)
  1236  	return b, err
  1237  }