// createTestEngine creates an in-memory engine and initializes some // default configuration settings. func createTestEngine(t *testing.T) engine.Engine { e := engine.NewInMem(engine.Attributes([]string{"dc1", "mem"}), 1<<20) if err := engine.PutI(e, engine.KeyConfigAccountingPrefix, testDefaultAcctConfig); err != nil { t.Fatal(err) } if err := engine.PutI(e, engine.KeyConfigPermissionPrefix, testDefaultPermConfig); err != nil { t.Fatal(err) } if err := engine.PutI(e, engine.KeyConfigZonePrefix, testDefaultZoneConfig); err != nil { t.Fatal(err) } return e }
// TestRangeGossipConfigWithMultipleKeyPrefixes verifies that multiple // key prefixes for a config are gossipped. func TestRangeGossipConfigWithMultipleKeyPrefixes(t *testing.T) { e := createTestEngine(t) // Add a permission for a new key prefix. db1Perm := PermConfig{ Read: []string{"spencer", "foo", "bar", "baz"}, Write: []string{"spencer"}, } key := engine.MakeKey(engine.KeyConfigPermissionPrefix, engine.Key("/db1")) if err := engine.PutI(e, key, db1Perm); err != nil { t.Fatal(err) } r, g := createTestRange(e, t) defer r.Stop() info, err := g.GetInfo(gossip.KeyConfigPermission) if err != nil { t.Fatal(err) } configMap := info.(PrefixConfigMap) expConfigs := []*PrefixConfig{ &PrefixConfig{engine.KeyMin, nil, &testDefaultPermConfig}, &PrefixConfig{engine.Key("/db1"), nil, &db1Perm}, &PrefixConfig{engine.Key("/db2"), engine.KeyMin, &testDefaultPermConfig}, } if !reflect.DeepEqual([]*PrefixConfig(configMap), expConfigs) { t.Errorf("expected gossiped configs to be equal %s vs %s", configMap, expConfigs) } }
// CreateRange allocates a new range ID and stores range metadata. // On success, returns the new range. func (s *Store) CreateRange(startKey, endKey engine.Key, replicas []Replica) (*Range, error) { rangeID, err := engine.Increment(s.engine, engine.KeyLocalRangeIDGenerator, 1) if err != nil { return nil, err } if ok, _ := engine.GetI(s.engine, makeRangeKey(rangeID), nil); ok { return nil, util.Error("newly allocated range ID already in use") } // RangeMetadata is stored local to this store only. It is neither // replicated via raft nor available via the global kv store. meta := RangeMetadata{ ClusterID: s.Ident.ClusterID, RangeID: rangeID, RangeDescriptor: RangeDescriptor{ StartKey: startKey, EndKey: endKey, Replicas: replicas, }, } err = engine.PutI(s.engine, makeRangeKey(rangeID), meta) if err != nil { return nil, err } rng := NewRange(meta, s.clock, s.engine, s.allocator, s.gossip) rng.Start() s.mu.Lock() defer s.mu.Unlock() s.ranges[rangeID] = rng return rng, nil }
// Bootstrap writes a new store ident to the underlying engine. To // ensure that no crufty data already exists in the engine, it scans // the engine contents before writing the new store ident. The engine // should be completely empty. It returns an error if called on a // non-empty engine. func (s *Store) Bootstrap(ident StoreIdent) error { s.Ident = ident kvs, err := s.engine.Scan(engine.KeyMin, engine.KeyMax, 1 /* only need one entry to fail! */) if err != nil { return util.Errorf("unable to scan engine to verify empty: %v", err) } else if len(kvs) > 0 { return util.Errorf("bootstrap failed; non-empty map with first key %q", kvs[0].Key) } return engine.PutI(s.engine, engine.KeyLocalIdent, s.Ident) }
// PutResponse writes a response to the cache for the specified cmdID. // The inflight entry corresponding to cmdID is removed from the // inflight map. Any requests waiting on the outcome of the inflight // command will be signaled to wakeup and read the command response // from the cache. func (rc *ResponseCache) PutResponse(cmdID ClientCmdID, reply interface{}) error { // Do nothing if command ID is empty. if cmdID.IsEmpty() { return nil } // Write the response value to the engine. key := rc.makeKey(cmdID) err := engine.PutI(rc.engine, key, reply) // Take lock after writing response to cache! rc.Lock() defer rc.Unlock() // Even on error, we remove the entry from the inflight map. rc.removeInflightLocked(cmdID) return err }
// HeartbeatTransaction updates the transaction status and heartbeat timestamp // on heartbeat message from a txn coordinator. The range will return the // current status of this transaction to the coordinator. func (r *Range) HeartbeatTransaction(args *HeartbeatTransactionRequest, reply *HeartbeatTransactionResponse) { var txn Transaction _, err := engine.GetI(r.engine, args.Key, &txn) if err != nil { reply.Error = err return } if txn.Status == PENDING { if !args.Timestamp.Less(txn.LastHeartbeat) { txn.LastHeartbeat = args.Timestamp } if err := engine.PutI(r.engine, args.Key, txn); err != nil { reply.Error = err return } } reply.Status = txn.Status }