github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/write_read_timezone_test.go (about)

     1  // +build integration
     2  
     3  // Copyright (c) 2017 Uber Technologies, Inc.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  package integration
    24  
    25  import (
    26  	"os"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/m3db/m3/src/x/ident"
    31  	xtime "github.com/m3db/m3/src/x/time"
    32  
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  type readWriteTZCase struct {
    37  	namespace  string
    38  	id         string
    39  	datapoints []readWriteTZDP
    40  }
    41  
    42  type readWriteTZDP struct {
    43  	value     float64
    44  	timestamp xtime.UnixNano
    45  }
    46  
    47  // Make sure that everything works properly end-to-end even if the client issues
    48  // a write in a timezone other than that of the server.
    49  func TestWriteReadTimezone(t *testing.T) {
    50  	if testing.Short() {
    51  		t.SkipNow()
    52  	}
    53  
    54  	// Ensure that the test is running with the local timezone set to US/Pacific.
    55  	// Note that we do this instead of just manipulating the NowFn on the test
    56  	// setup because there are ways to end up with a time object with a non-UTC
    57  	// timezone besides calling time.Now(). For example, converting a unix
    58  	// timestamp into a time.Time object will automatically associate the machine's
    59  	// local timezone.
    60  	os.Setenv("TZ", "US/Pacific")
    61  	name, offset := time.Now().Zone()
    62  	// The zone name will be PST or PDT depending on whether daylight savings
    63  	// is currently in effect
    64  	if name == "PDT" {
    65  		require.Equal(t, offset, -25200)
    66  	} else if name == "PST" {
    67  		require.Equal(t, offset, -28800)
    68  	} else {
    69  		t.Fatalf("Zone should be PDT or PST, but was: %s", name)
    70  	}
    71  
    72  	// Load locations that we'll need later in the tests
    73  	pacificLocation, err := time.LoadLocation("US/Pacific")
    74  	require.NoError(t, err)
    75  	nyLocation, err := time.LoadLocation("America/New_York")
    76  	require.NoError(t, err)
    77  
    78  	// Setup / start server
    79  	opts := NewTestOptions(t)
    80  	setup, err := NewTestSetup(t, opts, nil)
    81  	require.NoError(t, err)
    82  	defer setup.Close()
    83  	require.NoError(t, setup.StartServer())
    84  	require.NoError(t, setup.WaitUntilServerIsBootstrapped())
    85  
    86  	// Make sure that the server's internal clock function returns pacific timezone
    87  	start := setup.NowFn()().ToTime()
    88  	setup.SetNowFn(xtime.ToUnixNano(start.In(pacificLocation)))
    89  
    90  	// Instantiate a client
    91  	client := setup.M3DBClient()
    92  	session, err := client.DefaultSession()
    93  	require.NoError(t, err)
    94  	defer session.Close()
    95  
    96  	// Generate test datapoints (all with NY timezone)
    97  	namespace := opts.Namespaces()[0].ID().String()
    98  	startNy := xtime.ToUnixNano(start.In(nyLocation))
    99  	writeSeries := []readWriteTZCase{
   100  		{
   101  			namespace: namespace,
   102  			id:        "some-id-1",
   103  			datapoints: []readWriteTZDP{
   104  				{
   105  					value:     20.0,
   106  					timestamp: startNy,
   107  				},
   108  				{
   109  					value:     20.0,
   110  					timestamp: startNy.Add(1 * time.Second),
   111  				},
   112  				{
   113  					value:     20.0,
   114  					timestamp: startNy.Add(2 * time.Second),
   115  				},
   116  			},
   117  		},
   118  		{
   119  			namespace: namespace,
   120  			id:        "some-id-2",
   121  			datapoints: []readWriteTZDP{
   122  				{
   123  					value:     30.0,
   124  					timestamp: startNy,
   125  				},
   126  				{
   127  					value:     30.0,
   128  					timestamp: startNy.Add(1 * time.Second),
   129  				},
   130  				{
   131  					value:     30.0,
   132  					timestamp: startNy.Add(2 * time.Second),
   133  				},
   134  			},
   135  		},
   136  	}
   137  
   138  	// Write datapoints
   139  	for _, series := range writeSeries {
   140  		for _, write := range series.datapoints {
   141  			err = session.Write(ident.StringID(series.namespace), ident.StringID(series.id), write.timestamp, write.value, xtime.Second, nil)
   142  			require.NoError(t, err)
   143  		}
   144  	}
   145  
   146  	// Read datapoints back
   147  	iters, err := session.FetchIDs(ident.StringID(namespace),
   148  		ident.NewIDsIterator(ident.StringID("some-id-1"), ident.StringID("some-id-2")),
   149  		startNy, startNy.Add(1*time.Hour))
   150  	require.NoError(t, err)
   151  
   152  	// Assert datapoints match what we wrote
   153  	for i, iter := range iters.Iters() {
   154  		for j := 0; iter.Next(); j++ {
   155  			dp, _, _ := iter.Current()
   156  			expectedDatapoint := writeSeries[i].datapoints[j]
   157  			// Datapoints will comeback with the timezone set to the local timezone
   158  			// of the machine that the client is running on. The Equal() method ensures
   159  			// that the two time.Time struct's refer to the same instant in time
   160  			require.True(t, expectedDatapoint.timestamp.Equal(dp.TimestampNanos))
   161  			require.Equal(t, expectedDatapoint.value, dp.Value)
   162  		}
   163  	}
   164  }