github.com/m3db/m3@v1.5.0/examples/dbnode/proto_client/main.go (about)

     1  // Copyright (c) 2020 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 main
    22  
    23  import (
    24  	"flag"
    25  	"io/ioutil"
    26  	"log"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/jhump/protoreflect/desc"
    31  	"github.com/jhump/protoreflect/dynamic"
    32  	"github.com/m3db/m3/src/dbnode/client"
    33  	"github.com/m3db/m3/src/dbnode/encoding/proto"
    34  	"github.com/m3db/m3/src/x/ident"
    35  	xtime "github.com/m3db/m3/src/x/time"
    36  	yaml "gopkg.in/yaml.v2"
    37  )
    38  
    39  const (
    40  	namespace = "default"
    41  )
    42  
    43  var (
    44  	namespaceID = ident.StringID(namespace)
    45  )
    46  
    47  type config struct {
    48  	Client client.Configuration `yaml:"m3db_client"`
    49  }
    50  
    51  var configFile = flag.String("f", "", "configuration file")
    52  
    53  func main() {
    54  	flag.Parse()
    55  	if *configFile == "" {
    56  		flag.Usage()
    57  		return
    58  	}
    59  
    60  	cfgBytes, err := ioutil.ReadFile(*configFile)
    61  	if err != nil {
    62  		log.Fatalf("unable to read config file: %s, err: %v", *configFile, err)
    63  	}
    64  
    65  	cfg := &config{}
    66  	if err := yaml.UnmarshalStrict(cfgBytes, cfg); err != nil {
    67  		log.Fatalf("unable to parse YAML: %v", err)
    68  	}
    69  
    70  	// TODO(rartoul): Provide guidelines on reducing memory usage by tuning pooling options.
    71  	client, err := cfg.Client.NewClient(client.ConfigurationParameters{})
    72  	if err != nil {
    73  		log.Fatalf("unable to create new M3DB client: %v", err)
    74  	}
    75  
    76  	session, err := client.DefaultSession()
    77  	if err != nil {
    78  		log.Fatalf("unable to create new M3DB session: %v", err)
    79  	}
    80  	defer session.Close()
    81  
    82  	schemaConfig, ok := cfg.Client.Proto.SchemaRegistry[namespace]
    83  	if !ok {
    84  		log.Fatalf("schema path for namespace: %s not found", namespace)
    85  	}
    86  
    87  	// NB(rartoul): Using dynamic / reflection based library for marshaling and unmarshaling protobuf
    88  	// messages for simplicity, use generated message-specific bindings in production.
    89  	schema, err := proto.ParseProtoSchema(schemaConfig.SchemaFilePath, schemaConfig.MessageName)
    90  	if err != nil {
    91  		log.Fatalf("could not parse proto schema: %v", err)
    92  	}
    93  
    94  	runUntaggedExample(session, schema)
    95  	runTaggedExample(session, schema)
    96  	// TODO(rartoul): Add an aggregations query example.
    97  }
    98  
    99  // runUntaggedExample demonstrates how to write "untagged" (unindexed) data into M3DB with a given
   100  // protobuf schema and then read it back out again.
   101  func runUntaggedExample(session client.Session, schema *desc.MessageDescriptor) {
   102  	log.Printf("------ run untagged example ------")
   103  	var (
   104  		untaggedSeriesID = ident.StringID("untagged_seriesID")
   105  		m                = newTestValue(schema)
   106  	)
   107  	marshaled, err := m.Marshal()
   108  	if err != nil {
   109  		log.Fatalf("error marshaling protobuf message: %v", err)
   110  	}
   111  
   112  	// Write an untagged series ID. Pass 0 for value since it is ignored.
   113  	if err := session.Write(namespaceID, untaggedSeriesID, xtime.Now(), 0, xtime.Nanosecond, marshaled); err != nil {
   114  		log.Fatalf("unable to write untagged series: %v", err)
   115  	}
   116  
   117  	// Fetch data for the untagged seriesID written within the last minute.
   118  	seriesIter, err := session.Fetch(namespaceID, untaggedSeriesID, xtime.Now().Add(-time.Minute), xtime.Now())
   119  	if err != nil {
   120  		log.Fatalf("error fetching data for untagged series: %v", err)
   121  	}
   122  	for seriesIter.Next() {
   123  		m = dynamic.NewMessage(schema)
   124  		dp, _, marshaledProto := seriesIter.Current()
   125  		if err := m.Unmarshal(marshaledProto); err != nil {
   126  			log.Fatalf("error unmarshaling protobuf message: %v", err)
   127  		}
   128  		log.Printf("%s: %s", dp.TimestampNanos.String(), m.String())
   129  	}
   130  	if err := seriesIter.Err(); err != nil {
   131  		log.Fatalf("error in series iterator: %v", err)
   132  	}
   133  }
   134  
   135  // runTaggedExample demonstrates how to write "tagged" (indexed) data into M3DB with a given protobuf
   136  // schema and then read it back out again by either:
   137  //
   138  //   1. Querying for a specific time series by its ID directly
   139  //   2. TODO(rartoul): Querying for a set of time series using an inverted index query
   140  func runTaggedExample(session client.Session, schema *desc.MessageDescriptor) {
   141  	log.Printf("------ run tagged example ------")
   142  	var (
   143  		seriesID = ident.StringID("vehicle_id_1")
   144  		tags     = []ident.Tag{
   145  			{Name: ident.StringID("type"), Value: ident.StringID("sedan")},
   146  			{Name: ident.StringID("city"), Value: ident.StringID("san_francisco")},
   147  		}
   148  		tagsIter = ident.NewTagsIterator(ident.NewTags(tags...))
   149  		m        = newTestValue(schema)
   150  	)
   151  	marshaled, err := m.Marshal()
   152  	if err != nil {
   153  		log.Fatalf("error marshaling protobuf message: %v", err)
   154  	}
   155  
   156  	// Write a tagged series ID. Pass 0 for value since it is ignored.
   157  	if err := session.WriteTagged(namespaceID, seriesID, tagsIter, xtime.Now(), 0, xtime.Nanosecond, marshaled); err != nil {
   158  		log.Fatalf("error writing series %s, err: %v", seriesID.String(), err)
   159  	}
   160  
   161  	// Fetch data for the tagged seriesID using a direct ID lookup (only data written within the last minute).
   162  	seriesIter, err := session.Fetch(namespaceID, seriesID, xtime.Now().Add(-time.Minute), xtime.Now())
   163  	if err != nil {
   164  		log.Fatalf("error fetching data for untagged series: %v", err)
   165  	}
   166  	for seriesIter.Next() {
   167  		m = dynamic.NewMessage(schema)
   168  		dp, _, marshaledProto := seriesIter.Current()
   169  		if err := m.Unmarshal(marshaledProto); err != nil {
   170  			log.Fatalf("error unamrshaling protobuf message: %v", err)
   171  		}
   172  		log.Printf("%s: %s", dp.TimestampNanos.String(), m.String())
   173  	}
   174  	if err := seriesIter.Err(); err != nil {
   175  		log.Fatalf("error in series iterator: %v", err)
   176  	}
   177  
   178  	// TODO(rartoul): Show an example of how to execute a FetchTagged() call with an index query.
   179  }
   180  
   181  var (
   182  	testValueLock  sync.Mutex
   183  	testValueCount = 1
   184  )
   185  
   186  func newTestValue(schema *desc.MessageDescriptor) *dynamic.Message {
   187  	testValueLock.Lock()
   188  	defer testValueLock.Unlock()
   189  
   190  	m := dynamic.NewMessage(schema)
   191  	m.SetFieldByName("latitude", float64(testValueCount))
   192  	m.SetFieldByName("longitude", float64(testValueCount))
   193  	m.SetFieldByName("fuel_percent", 0.75)
   194  	m.SetFieldByName("status", "active")
   195  
   196  	testValueCount++
   197  
   198  	return m
   199  }