github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/datas/puller_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     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 datas
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"errors"
    21  	"os"
    22  	"path/filepath"
    23  	"sync"
    24  	"testing"
    25  
    26  	"github.com/google/uuid"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"github.com/dolthub/dolt/go/store/nbs"
    31  	"github.com/dolthub/dolt/go/store/types"
    32  	"github.com/dolthub/dolt/go/store/util/clienttest"
    33  )
    34  
    35  func mustTuple(tpl types.Tuple, err error) types.Tuple {
    36  	if err != nil {
    37  		panic(err)
    38  	}
    39  
    40  	return tpl
    41  }
    42  
    43  func addTableValues(ctx context.Context, vrw types.ValueReadWriter, m types.Map, tableName string, alternatingKeyVals ...types.Value) (types.Map, error) {
    44  	val, ok, err := m.MaybeGet(ctx, types.String(tableName))
    45  
    46  	if err != nil {
    47  		return types.EmptyMap, err
    48  	}
    49  
    50  	var tblMap types.Map
    51  	if ok {
    52  		mv, err := val.(types.Ref).TargetValue(ctx, vrw)
    53  
    54  		if err != nil {
    55  			return types.EmptyMap, err
    56  		}
    57  
    58  		me := mv.(types.Map).Edit()
    59  
    60  		for i := 0; i < len(alternatingKeyVals); i += 2 {
    61  			me.Set(alternatingKeyVals[i], alternatingKeyVals[i+1])
    62  		}
    63  
    64  		tblMap, err = me.Map(ctx)
    65  
    66  		if err != nil {
    67  			return types.EmptyMap, err
    68  		}
    69  	} else {
    70  		tblMap, err = types.NewMap(ctx, vrw, alternatingKeyVals...)
    71  
    72  		if err != nil {
    73  			return types.EmptyMap, err
    74  		}
    75  	}
    76  
    77  	tblRef, err := writeValAndGetRef(ctx, vrw, tblMap)
    78  
    79  	if err != nil {
    80  		return types.EmptyMap, err
    81  	}
    82  
    83  	me := m.Edit()
    84  	me.Set(types.String(tableName), tblRef)
    85  	return me.Map(ctx)
    86  }
    87  
    88  func deleteTableValues(ctx context.Context, vrw types.ValueReadWriter, m types.Map, tableName string, keys ...types.Value) (types.Map, error) {
    89  	if len(keys) == 0 {
    90  		return m, nil
    91  	}
    92  
    93  	val, ok, err := m.MaybeGet(ctx, types.String(tableName))
    94  
    95  	if err != nil {
    96  		return types.EmptyMap, err
    97  	}
    98  
    99  	if !ok {
   100  		return types.EmptyMap, errors.New("can't delete from table that wasn't created")
   101  	}
   102  
   103  	mv, err := val.(types.Ref).TargetValue(ctx, vrw)
   104  
   105  	if err != nil {
   106  		return types.EmptyMap, err
   107  	}
   108  
   109  	me := mv.(types.Map).Edit()
   110  	for _, k := range keys {
   111  		me.Remove(k)
   112  	}
   113  
   114  	tblMap, err := me.Map(ctx)
   115  
   116  	if err != nil {
   117  		return types.EmptyMap, err
   118  	}
   119  
   120  	tblRef, err := writeValAndGetRef(ctx, vrw, tblMap)
   121  
   122  	if err != nil {
   123  		return types.EmptyMap, err
   124  	}
   125  
   126  	me = m.Edit()
   127  	me.Set(types.String(tableName), tblRef)
   128  	return me.Map(ctx)
   129  }
   130  
   131  func tempDirDB(ctx context.Context) (Database, error) {
   132  	dir := filepath.Join(os.TempDir(), uuid.New().String())
   133  	err := os.MkdirAll(dir, os.ModePerm)
   134  
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	st, err := nbs.NewLocalStore(ctx, types.Format_Default.VersionString(), dir, clienttest.DefaultMemTableSize)
   140  
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	return NewDatabase(st), nil
   146  }
   147  
   148  func TestPuller(t *testing.T) {
   149  	deltas := []struct {
   150  		name       string
   151  		sets       map[string][]types.Value
   152  		deletes    map[string][]types.Value
   153  		tblDeletes []string
   154  	}{
   155  		{
   156  			"empty",
   157  			map[string][]types.Value{},
   158  			map[string][]types.Value{},
   159  			[]string{},
   160  		},
   161  		{
   162  			"employees",
   163  			map[string][]types.Value{
   164  				"employees": {
   165  					mustTuple(types.NewTuple(types.Format_Default, types.String("Hendriks"), types.String("Brian"))),
   166  					mustTuple(types.NewTuple(types.Format_Default, types.String("Software Engineer"), types.Int(39))),
   167  					mustTuple(types.NewTuple(types.Format_Default, types.String("Sehn"), types.String("Timothy"))),
   168  					mustTuple(types.NewTuple(types.Format_Default, types.String("CEO"), types.Int(39))),
   169  					mustTuple(types.NewTuple(types.Format_Default, types.String("Son"), types.String("Aaron"))),
   170  					mustTuple(types.NewTuple(types.Format_Default, types.String("Software Engineer"), types.Int(36))),
   171  				},
   172  			},
   173  			map[string][]types.Value{},
   174  			[]string{},
   175  		},
   176  		{
   177  			"ip to country",
   178  			map[string][]types.Value{
   179  				"ip_to_country": {
   180  					types.String("5.183.230.1"), types.String("BZ"),
   181  					types.String("5.180.188.1"), types.String("AU"),
   182  					types.String("2.56.9.244"), types.String("GB"),
   183  					types.String("20.175.7.56"), types.String("US"),
   184  				},
   185  			},
   186  			map[string][]types.Value{},
   187  			[]string{},
   188  		},
   189  		{
   190  			"more ips",
   191  			map[string][]types.Value{
   192  				"ip_to_country": {
   193  					types.String("20.175.193.85"), types.String("US"),
   194  					types.String("5.196.110.191"), types.String("FR"),
   195  					types.String("4.14.242.160"), types.String("CA"),
   196  				},
   197  			},
   198  			map[string][]types.Value{},
   199  			[]string{},
   200  		},
   201  		{
   202  			"more employees",
   203  			map[string][]types.Value{
   204  				"employees": {
   205  					mustTuple(types.NewTuple(types.Format_Default, types.String("Jesuele"), types.String("Matt"))),
   206  					mustTuple(types.NewTuple(types.Format_Default, types.String("Software Engineer"), types.NullValue)),
   207  					mustTuple(types.NewTuple(types.Format_Default, types.String("Wilkins"), types.String("Daylon"))),
   208  					mustTuple(types.NewTuple(types.Format_Default, types.String("Software Engineer"), types.NullValue)),
   209  					mustTuple(types.NewTuple(types.Format_Default, types.String("Katie"), types.String("McCulloch"))),
   210  					mustTuple(types.NewTuple(types.Format_Default, types.String("Software Engineer"), types.NullValue)),
   211  				},
   212  			},
   213  			map[string][]types.Value{},
   214  			[]string{},
   215  		},
   216  		{
   217  			"delete ips table",
   218  			map[string][]types.Value{},
   219  			map[string][]types.Value{},
   220  			[]string{"ip_to_country"},
   221  		},
   222  		{
   223  			"delete some employees",
   224  			map[string][]types.Value{},
   225  			map[string][]types.Value{
   226  				"employees": {
   227  					mustTuple(types.NewTuple(types.Format_Default, types.String("Hendriks"), types.String("Brian"))),
   228  					mustTuple(types.NewTuple(types.Format_Default, types.String("Sehn"), types.String("Timothy"))),
   229  					mustTuple(types.NewTuple(types.Format_Default, types.String("Son"), types.String("Aaron"))),
   230  				},
   231  			},
   232  			[]string{},
   233  		},
   234  	}
   235  
   236  	ctx := context.Background()
   237  	db, err := tempDirDB(ctx)
   238  	require.NoError(t, err)
   239  	ds, err := db.GetDataset(ctx, "ds")
   240  	require.NoError(t, err)
   241  	rootMap, err := types.NewMap(ctx, db)
   242  	require.NoError(t, err)
   243  
   244  	parent, err := types.NewList(ctx, db)
   245  	require.NoError(t, err)
   246  	states := map[string]types.Ref{}
   247  	for _, delta := range deltas {
   248  		for tbl, sets := range delta.sets {
   249  			rootMap, err = addTableValues(ctx, db, rootMap, tbl, sets...)
   250  			require.NoError(t, err)
   251  		}
   252  
   253  		for tbl, dels := range delta.deletes {
   254  			rootMap, err = deleteTableValues(ctx, db, rootMap, tbl, dels...)
   255  			require.NoError(t, err)
   256  		}
   257  
   258  		me := rootMap.Edit()
   259  		for _, tbl := range delta.tblDeletes {
   260  			me.Remove(types.String(tbl))
   261  		}
   262  		rootMap, err = me.Map(ctx)
   263  		require.NoError(t, err)
   264  
   265  		commitOpts := CommitOptions{ParentsList: parent}
   266  		ds, err = db.Commit(ctx, ds, rootMap, commitOpts)
   267  		require.NoError(t, err)
   268  
   269  		r, ok, err := ds.MaybeHeadRef()
   270  		require.NoError(t, err)
   271  		require.True(t, ok)
   272  
   273  		parent, err = types.NewList(ctx, db, r)
   274  		require.NoError(t, err)
   275  
   276  		states[delta.name] = r
   277  	}
   278  
   279  	tbl, err := makeABigTable(ctx, db)
   280  	require.NoError(t, err)
   281  
   282  	tblRef, err := writeValAndGetRef(ctx, db, tbl)
   283  	require.NoError(t, err)
   284  
   285  	me := rootMap.Edit()
   286  	me.Set(types.String("big_table"), tblRef)
   287  	rootMap, err = me.Map(ctx)
   288  	require.NoError(t, err)
   289  
   290  	commitOpts := CommitOptions{ParentsList: parent}
   291  	ds, err = db.Commit(ctx, ds, rootMap, commitOpts)
   292  	require.NoError(t, err)
   293  
   294  	r, ok, err := ds.MaybeHeadRef()
   295  	require.NoError(t, err)
   296  	require.True(t, ok)
   297  
   298  	states["add big table"] = r
   299  
   300  	for k, rootRef := range states {
   301  		t.Run(k, func(t *testing.T) {
   302  			eventCh := make(chan PullerEvent, 128)
   303  			wg := new(sync.WaitGroup)
   304  			wg.Add(1)
   305  			go func() {
   306  				defer wg.Done()
   307  				for evt := range eventCh {
   308  					var details interface{}
   309  					switch evt.EventType {
   310  					case NewLevelTWEvent, DestDBHasTWEvent, LevelUpdateTWEvent:
   311  						details = evt.TWEventDetails
   312  					default:
   313  						details = evt.TFEventDetails
   314  					}
   315  
   316  					jsonBytes, err := json.Marshal(details)
   317  
   318  					if err == nil {
   319  						t.Logf("event_type: %d details: %s\n", evt.EventType, string(jsonBytes))
   320  					}
   321  				}
   322  			}()
   323  
   324  			sinkdb, err := tempDirDB(ctx)
   325  			require.NoError(t, err)
   326  
   327  			tmpDir := filepath.Join(os.TempDir(), uuid.New().String())
   328  			err = os.MkdirAll(tmpDir, os.ModePerm)
   329  			require.NoError(t, err)
   330  			plr, err := NewPuller(ctx, tmpDir, 128, db, sinkdb, rootRef.TargetHash(), eventCh)
   331  			require.NoError(t, err)
   332  
   333  			err = plr.Pull(ctx)
   334  			close(eventCh)
   335  			require.NoError(t, err)
   336  			wg.Wait()
   337  
   338  			sinkDS, err := sinkdb.GetDataset(ctx, "ds")
   339  			require.NoError(t, err)
   340  			sinkDS, err = sinkdb.FastForward(ctx, sinkDS, rootRef)
   341  			require.NoError(t, err)
   342  
   343  			require.NoError(t, err)
   344  			sinkRootRef, ok, err := sinkDS.MaybeHeadRef()
   345  			require.NoError(t, err)
   346  			require.True(t, ok)
   347  
   348  			eq, err := pullerRefEquality(ctx, rootRef, sinkRootRef, db, sinkdb)
   349  			require.NoError(t, err)
   350  			assert.True(t, eq)
   351  
   352  		})
   353  	}
   354  }
   355  
   356  func makeABigTable(ctx context.Context, db Database) (types.Map, error) {
   357  	m, err := types.NewMap(ctx, db)
   358  
   359  	if err != nil {
   360  		return types.EmptyMap, nil
   361  	}
   362  
   363  	me := m.Edit()
   364  
   365  	for i := 0; i < 256*1024; i++ {
   366  		tpl, err := types.NewTuple(db.Format(), types.UUID(uuid.New()), types.String(uuid.New().String()), types.Float(float64(i)))
   367  
   368  		if err != nil {
   369  			return types.EmptyMap, err
   370  		}
   371  
   372  		me.Set(types.Int(i), tpl)
   373  	}
   374  
   375  	return me.Map(ctx)
   376  }
   377  
   378  func pullerRefEquality(ctx context.Context, expectad, actual types.Ref, srcDB, sinkDB Database) (bool, error) {
   379  	expectedVal, err := expectad.TargetValue(ctx, srcDB)
   380  
   381  	if err != nil {
   382  		return false, err
   383  	}
   384  
   385  	actualVal, err := actual.TargetValue(ctx, sinkDB)
   386  	if err != nil {
   387  		return false, err
   388  	}
   389  
   390  	exPs, exTbls, err := parentsAndTables(expectedVal.(types.Struct))
   391  	if err != nil {
   392  		return false, err
   393  	}
   394  
   395  	actPs, actTbls, err := parentsAndTables(actualVal.(types.Struct))
   396  	if err != nil {
   397  		return false, err
   398  	}
   399  
   400  	if !exPs.Equals(actPs) {
   401  		return false, nil
   402  	}
   403  
   404  	err = exTbls.IterAll(ctx, func(key, exVal types.Value) error {
   405  		actVal, ok, err := actTbls.MaybeGet(ctx, key)
   406  
   407  		if err != nil {
   408  			return err
   409  		}
   410  
   411  		if !ok {
   412  			return errors.New("Missing table " + string(key.(types.String)))
   413  		}
   414  
   415  		exMapVal, err := exVal.(types.Ref).TargetValue(ctx, srcDB)
   416  
   417  		if err != nil {
   418  			return err
   419  		}
   420  
   421  		actMapVal, err := actVal.(types.Ref).TargetValue(ctx, sinkDB)
   422  
   423  		if err != nil {
   424  			return err
   425  		}
   426  
   427  		return errIfNotEqual(ctx, exMapVal.(types.Map), actMapVal.(types.Map))
   428  	})
   429  
   430  	if err != nil {
   431  		return false, err
   432  	}
   433  
   434  	return exTbls.Equals(actTbls), nil
   435  }
   436  
   437  var errNotEqual = errors.New("not equal")
   438  
   439  func errIfNotEqual(ctx context.Context, ex, act types.Map) error {
   440  	exItr, err := ex.Iterator(ctx)
   441  
   442  	if err != nil {
   443  		return err
   444  	}
   445  
   446  	actItr, err := act.Iterator(ctx)
   447  
   448  	if err != nil {
   449  		return err
   450  	}
   451  
   452  	for {
   453  		exK, exV, err := exItr.Next(ctx)
   454  
   455  		if err != nil {
   456  			return err
   457  		}
   458  
   459  		actK, actV, err := actItr.Next(ctx)
   460  
   461  		if err != nil {
   462  			return err
   463  		}
   464  
   465  		if actK == nil && exK == nil {
   466  			break
   467  		} else if exK == nil || actK == nil {
   468  			return errNotEqual
   469  		}
   470  
   471  		if exV == nil && actV == nil {
   472  			continue
   473  		} else if exV == nil || actV == nil {
   474  			return errNotEqual
   475  		}
   476  
   477  		if !exK.Equals(actK) || !exV.Equals(actV) {
   478  			return errNotEqual
   479  		}
   480  	}
   481  
   482  	return nil
   483  }
   484  
   485  func parentsAndTables(cm types.Struct) (types.List, types.Map, error) {
   486  	ps, ok, err := cm.MaybeGet(ParentsListField)
   487  
   488  	if err != nil {
   489  		return types.EmptyList, types.EmptyMap, err
   490  	}
   491  
   492  	if !ok {
   493  		return types.EmptyList, types.EmptyMap, err
   494  	}
   495  
   496  	tbls, ok, err := cm.MaybeGet("value")
   497  
   498  	if err != nil {
   499  		return types.EmptyList, types.EmptyMap, err
   500  	}
   501  
   502  	if !ok {
   503  		return types.EmptyList, types.EmptyMap, err
   504  	}
   505  
   506  	return ps.(types.List), tbls.(types.Map), nil
   507  }
   508  
   509  func writeValAndGetRef(ctx context.Context, vrw types.ValueReadWriter, val types.Value) (types.Ref, error) {
   510  	valRef, err := types.NewRef(val, vrw.Format())
   511  
   512  	if err != nil {
   513  		return types.Ref{}, err
   514  	}
   515  
   516  	targetVal, err := valRef.TargetValue(ctx, vrw)
   517  
   518  	if err != nil {
   519  		return types.Ref{}, err
   520  	}
   521  
   522  	if targetVal == nil {
   523  		_, err = vrw.WriteValue(ctx, val)
   524  
   525  		if err != nil {
   526  			return types.Ref{}, err
   527  		}
   528  	}
   529  
   530  	return valRef, err
   531  }