github.com/m3db/m3@v1.5.0/src/cluster/mem/mem.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 memcluster
    22  
    23  import (
    24  	"errors"
    25  	"sync"
    26  
    27  	"github.com/m3db/m3/src/cluster/client"
    28  	"github.com/m3db/m3/src/cluster/kv"
    29  	"github.com/m3db/m3/src/cluster/kv/mem"
    30  	"github.com/m3db/m3/src/cluster/services"
    31  )
    32  
    33  const (
    34  	_kvPrefix = "_kv"
    35  )
    36  
    37  var (
    38  	// assert the interface matches.
    39  	_ client.Client = (*Client)(nil)
    40  )
    41  
    42  // Client provides a cluster/client.Client backed by kv/mem transaction store,
    43  // which stores data in memory instead of in etcd.
    44  type Client struct {
    45  	mu          sync.Mutex
    46  	serviceOpts kv.OverrideOptions
    47  	cache       map[cacheKey]kv.TxnStore
    48  }
    49  
    50  // New instantiates a client which defaults its stores to the given zone/env/namespace.
    51  func New(serviceOpts kv.OverrideOptions) *Client {
    52  	return &Client{
    53  		serviceOpts: serviceOpts,
    54  		cache:       make(map[cacheKey]kv.TxnStore),
    55  	}
    56  }
    57  
    58  // Services constructs a gateway to all cluster services, backed by a mem store.
    59  func (c *Client) Services(opts services.OverrideOptions) (services.Services, error) {
    60  	if opts == nil {
    61  		opts = services.NewOverrideOptions()
    62  	}
    63  
    64  	errUnsupported := errors.New("currently unsupported for inMemoryClusterClient")
    65  
    66  	kvGen := func(zone string) (kv.Store, error) {
    67  		return c.Store(kv.NewOverrideOptions().SetZone(zone))
    68  	}
    69  
    70  	heartbeatGen := func(sid services.ServiceID) (services.HeartbeatService, error) {
    71  		return nil, errUnsupported
    72  	}
    73  
    74  	leaderGen := func(sid services.ServiceID, opts services.ElectionOptions) (services.LeaderService, error) {
    75  		return nil, errUnsupported
    76  	}
    77  
    78  	return services.NewServices(
    79  		services.NewOptions().
    80  			SetKVGen(kvGen).
    81  			SetHeartbeatGen(heartbeatGen).
    82  			SetLeaderGen(leaderGen).
    83  			SetNamespaceOptions(opts.NamespaceOptions()),
    84  	)
    85  }
    86  
    87  // KV returns/constructs a mem backed kv.Store for the default zone/env/namespace.
    88  func (c *Client) KV() (kv.Store, error) {
    89  	return c.TxnStore(kv.NewOverrideOptions())
    90  }
    91  
    92  // Txn returns/constructs a mem backed kv.TxnStore for the default zone/env/namespace.
    93  func (c *Client) Txn() (kv.TxnStore, error) {
    94  	return c.TxnStore(kv.NewOverrideOptions())
    95  }
    96  
    97  // Store returns/constructs a mem backed kv.Store for the given env/zone/namespace.
    98  func (c *Client) Store(opts kv.OverrideOptions) (kv.Store, error) {
    99  	return c.TxnStore(opts)
   100  }
   101  
   102  // TxnStore returns/constructs a mem backed kv.TxnStore for the given env/zone/namespace.
   103  func (c *Client) TxnStore(opts kv.OverrideOptions) (kv.TxnStore, error) {
   104  	c.mu.Lock()
   105  	defer c.mu.Unlock()
   106  
   107  	opts = mergeOpts(c.serviceOpts, opts)
   108  	key := cacheKey{
   109  		Env:       opts.Environment(),
   110  		Zone:      opts.Zone(),
   111  		Namespace: opts.Namespace(),
   112  	}
   113  	if s, ok := c.cache[key]; ok {
   114  		return s, nil
   115  	}
   116  
   117  	store := mem.NewStore()
   118  	c.cache[key] = store
   119  	return store, nil
   120  }
   121  
   122  type cacheKey struct {
   123  	Env       string
   124  	Zone      string
   125  	Namespace string
   126  }
   127  
   128  func mergeOpts(defaults kv.OverrideOptions, opts kv.OverrideOptions) kv.OverrideOptions {
   129  	if opts.Zone() == "" {
   130  		opts = opts.SetZone(defaults.Zone())
   131  	}
   132  
   133  	if opts.Environment() == "" {
   134  		opts = opts.SetEnvironment(defaults.Environment())
   135  	}
   136  
   137  	if opts.Namespace() == "" {
   138  		opts = opts.SetNamespace(_kvPrefix)
   139  	}
   140  
   141  	return opts
   142  }