github.com/m3db/m3@v1.5.0/src/integration/repair/repair_and_replication.go (about)

     1  // Copyright (c) 2021  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 repair contains integration tests for repair and replication among dbnodes.
    22  package repair
    23  
    24  import (
    25  	"errors"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/stretchr/testify/require"
    30  
    31  	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
    32  	"github.com/m3db/m3/src/integration/resources"
    33  )
    34  
    35  const (
    36  	// TestRepairDBNodeConfig is the test config for the dbnode.
    37  	TestRepairDBNodeConfig = `
    38  db:
    39    repair:
    40      enabled: true
    41      throttle: 1ms
    42      checkInterval: 1ms
    43    replication:
    44      clusters:
    45        - name: "the_other_cluster"
    46          repairEnabled: true
    47          client:
    48            config:
    49              service:
    50                env: default_env
    51                zone: embedded
    52                service: m3db
    53                cacheDir: /var/lib/m3kv
    54                etcdClusters:
    55                  - zone: embedded
    56                    endpoints:
    57                      - the_other_cluster_node:2379
    58  `
    59  
    60  	// TestRepairCoordinatorConfig is the test config for the coordinator.
    61  	TestRepairCoordinatorConfig = `
    62  clusters:
    63    - namespaces:
    64        - namespace: coldWritesRepairAndNoIndex
    65          type: unaggregated
    66          retention: 4h
    67  `
    68  )
    69  
    70  // RunTest contains the logic for running the repair and replication test.
    71  func RunTest(t *testing.T, cluster1, cluster2 resources.M3Resources) {
    72  	var (
    73  		namespace = "coldWritesRepairAndNoIndex"
    74  		id        = "foo"
    75  	)
    76  	// write data for 'now - 2 * blockSize' to node 0 in cluster1
    77  	require.NoError(t, writeData(
    78  		namespace,
    79  		id,
    80  		time.Now().Add(-2*time.Hour),
    81  		12.3456789,
    82  		cluster1.Nodes()[0],
    83  	))
    84  
    85  	// verify the datapoint is present in the node
    86  	results, err := fetchData(
    87  		namespace,
    88  		id,
    89  		cluster1.Nodes()[0],
    90  	)
    91  	require.NoError(t, err)
    92  	require.Equal(t, 1, len(results))
    93  
    94  	// test repair
    95  	require.NoError(t, resources.Retry(verifyDataPoint(cluster1.Nodes()[1], namespace, id)))
    96  
    97  	// test replication
    98  	require.NoError(t, resources.Retry(verifyDataPoint(cluster2.Nodes()[0], namespace, id)))
    99  	require.NoError(t, resources.Retry(verifyDataPoint(cluster2.Nodes()[1], namespace, id)))
   100  }
   101  
   102  func verifyDataPoint(dbnode resources.Node, namespace, id string) func() error {
   103  	return func() error {
   104  		dp, err := fetchData(namespace, id, dbnode)
   105  		if err != nil {
   106  			return err
   107  		}
   108  		if len(dp) == 0 {
   109  			return errors.New("no datapoints")
   110  		}
   111  		return nil
   112  	}
   113  }
   114  
   115  func writeData(namespace, id string, ts time.Time, value float64, dbnode resources.Node) error {
   116  	return dbnode.WritePoint(&rpc.WriteRequest{
   117  		NameSpace: namespace,
   118  		ID:        id,
   119  		Datapoint: &rpc.Datapoint{
   120  			Timestamp:         ts.UnixNano(),
   121  			TimestampTimeType: rpc.TimeType_UNIX_NANOSECONDS,
   122  			Value:             value,
   123  		},
   124  	})
   125  }
   126  
   127  func fetchData(namespace, id string, dbnode resources.Node) ([]*rpc.Datapoint, error) {
   128  	result, err := dbnode.Fetch(&rpc.FetchRequest{
   129  		NameSpace:  namespace,
   130  		ID:         id,
   131  		RangeStart: 0,
   132  		RangeEnd:   time.Now().UnixNano(),
   133  		RangeType:  rpc.TimeType_UNIX_NANOSECONDS,
   134  	})
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	if result == nil {
   140  		return nil, nil
   141  	}
   142  
   143  	return result.Datapoints, err
   144  }