github.com/janelia-flyem/dvid@v1.0.0/datatype/keyvalue/keyvalue.go (about)

     1  /*
     2  	Package keyvalue implements DVID support for data using generic key-value.
     3  */
     4  package keyvalue
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"encoding/gob"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"strings"
    15  	"time"
    16  
    17  	pb "google.golang.org/protobuf/proto"
    18  
    19  	"github.com/janelia-flyem/dvid/datastore"
    20  	"github.com/janelia-flyem/dvid/datatype/common/proto"
    21  	"github.com/janelia-flyem/dvid/dvid"
    22  	"github.com/janelia-flyem/dvid/server"
    23  	"github.com/janelia-flyem/dvid/storage"
    24  )
    25  
    26  const (
    27  	Version  = "0.2"
    28  	RepoURL  = "github.com/janelia-flyem/dvid/datatype/keyvalue"
    29  	TypeName = "keyvalue"
    30  )
    31  
    32  const helpMessage = `
    33  API for 'keyvalue' datatype (github.com/janelia-flyem/dvid/datatype/keyvalue)
    34  =============================================================================
    35  
    36  Note: UUIDs referenced below are strings that may either be a unique prefix of a
    37  hexadecimal UUID string (e.g., 3FA22) or a branch leaf specification that adds
    38  a colon (":") followed by the case-dependent branch name.  In the case of a
    39  branch leaf specification, the unique UUID prefix just identifies the repo of
    40  the branch, and the UUID referenced is really the leaf of the branch name.
    41  For example, if we have a DAG with root A -> B -> C where C is the current
    42  HEAD or leaf of the "master" (default) branch, then asking for "B:master" is
    43  the same as asking for "C".  If we add another version so A -> B -> C -> D, then
    44  references to "B:master" now return the data from "D".
    45  
    46  Command-line:
    47  
    48  $ dvid repo <UUID> new keyvalue <data name> <settings...>
    49  
    50  	Adds newly named key-value data to repo with specified UUID.
    51  
    52  	Example:
    53  
    54  	$ dvid repo 3f8c new keyvalue stuff
    55  
    56  	Arguments:
    57  
    58  	UUID           Hexadecimal string with enough characters to uniquely identify a version node.
    59  	data name      Name of data to create, e.g., "myblobs"
    60  	settings       Configuration settings in "key=value" format separated by spaces.
    61  
    62  	Configuration Settings (case-insensitive keys):
    63  
    64  	Versioned      Set to "false" or "0" if the keyvalue instance is unversioned (repo-wide).
    65  				   An unversioned keyvalue will only use the UUIDs to look up the repo and
    66  				   not differentiate between versions in the same repo.  Note that unlike
    67  				   versioned data, distribution (push/pull) of unversioned data is not defined 
    68  				   at this time.
    69  
    70  $ dvid -stdin node <UUID> <data name> put <key> < data
    71  
    72  	Puts stdin data into the keyvalue data instance under the given key.
    73  
    74  	
    75  	------------------
    76  
    77  HTTP API (Level 2 REST):
    78  
    79  Note that browsers support HTTP PUT and DELETE via javascript but only GET/POST are
    80  included in HTML specs.  For ease of use in constructing clients, HTTP POST is used
    81  to create or modify resources in an idempotent fashion.
    82  
    83  GET  <api URL>/node/<UUID>/<data name>/help
    84  
    85  	Returns data-specific help message.
    86  
    87  
    88  GET  <api URL>/node/<UUID>/<data name>/info
    89  POST <api URL>/node/<UUID>/<data name>/info
    90  
    91  	Retrieves or puts data properties.
    92  
    93  	Example: 
    94  
    95  	GET <api URL>/node/3f8c/stuff/info
    96  
    97  	Returns JSON with configuration settings.
    98  
    99  	Arguments:
   100  
   101  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   102  	data name     Name of keyvalue data instance.
   103  
   104  GET <api URL>/node/<UUID>/<data name>/tags
   105  POST <api URL>/node/<UUID>/<data name>/tags?<options>
   106  
   107  	GET retrieves JSON of tags for this instance.
   108  	POST appends or replaces tags provided in POST body.  Expects JSON to be POSTed
   109  	with the following format:
   110  
   111  	{ "tag1": "anything you want", "tag2": "something else" }
   112  
   113  	To delete tags, pass an empty object with query string "replace=true".
   114  
   115  	POST Query-string Options:
   116  
   117  	replace   Set to "true" if you want passed tags to replace and not be appended to current tags.
   118  				Default operation is false (append).
   119  			   	
   120  GET  <api URL>/node/<UUID>/<data name>/keys
   121  
   122  	Returns all keys for this data instance in JSON format:
   123  
   124  	[key1, key2, ...]
   125  
   126  GET  <api URL>/node/<UUID>/<data name>/keyrange/<key1>/<key2>
   127  
   128  	Returns all keys between 'key1' and 'key2' for this data instance in JSON format:
   129  
   130  	[key1, key2, ...]
   131  
   132  	Arguments:
   133  
   134  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   135  	data name     Name of keyvalue data instance.
   136  	key1          Lexicographically lowest alphanumeric key in range.
   137  	key2          Lexicographically highest alphanumeric key in range.
   138  
   139  GET  <api URL>/node/<UUID>/<data name>/keyrangevalues/<key1>/<key2>?<options>
   140  
   141  	This has the same response as the GET /keyvalues endpoint but a different way of
   142  	specifying the keys.  In this endpoint, you specify a range of keys.  In the other
   143  	endpoint, you must explicitly send the keys in a GET payload, which may not be
   144  	fully supported.
   145  
   146  	Note that this endpoint streams data to the requester, which prevents setting HTTP
   147  	status to error if the streaming has already started.  Instead, malformed output
   148  	will inform the requester of an error.
   149  
   150  	Response types:
   151  
   152  	1) json (values are expected to be valid JSON or an error is returned)
   153  
   154  		{
   155  			"key1": value1,
   156  			"key2": value2,
   157  			...
   158  		}
   159  
   160  	2) tar
   161  
   162  		A tarfile is returned with each keys specifying the file names and
   163  		values as the file bytes.
   164  
   165  	3) protobuf3
   166  	
   167  		KeyValue data needs to be serialized in a format defined by the following 
   168  		protobuf3 definitions:
   169  
   170  		message KeyValue {
   171  			string key = 1;
   172  			bytes value = 2;
   173  		}
   174  
   175  		message KeyValues {
   176  			repeated KeyValue kvs = 1;
   177  		}
   178  
   179  	Arguments:
   180  
   181  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   182  	data name     Name of keyvalue data instance.
   183  	key1          Lexicographically lowest alphanumeric key in range.
   184  	key2          Lexicographically highest alphanumeric key in range.
   185  
   186  	GET Query-string Options (only one of these allowed):
   187  
   188  	json        If set to "true", the response will be JSON as above and the values must
   189  				  be valid JSON or an error will be returned.
   190  	tar			If set to "true", the response will be a tarfile with keys as file names.
   191  	protobuf	Default, or can be set to "true". Response will be protobuf KeyValues response
   192  
   193  	Additional query option:
   194  
   195  	check		If json=true, setting check=false will tell server to trust that the
   196  				  values will be valid JSON instead of parsing it as a check.
   197  
   198  
   199  GET  <api URL>/node/<UUID>/<data name>/key/<key>
   200  POST <api URL>/node/<UUID>/<data name>/key/<key>
   201  DEL  <api URL>/node/<UUID>/<data name>/key/<key> 
   202  HEAD <api URL>/node/<UUID>/<data name>/key/<key> 
   203  
   204  	Performs operations on a key-value pair depending on the HTTP verb.  
   205  
   206  	Example: 
   207  
   208  	GET <api URL>/node/3f8c/stuff/key/myfile.dat
   209  
   210  	Returns the data associated with the key "myfile.dat" of instance "stuff" in version
   211  	node 3f8c.
   212  
   213  	The "Content-type" of the HTTP response (and usually the request) are
   214  	"application/octet-stream" for arbitrary binary data.
   215  
   216  	For HEAD returns:
   217  	200 (OK) if a sparse volume of the given label exists within any optional bounds.
   218  	404 (File not Found) if there is no sparse volume for the given label within any optional bounds.
   219  
   220  	Arguments:
   221  
   222  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   223  	data name     Name of keyvalue data instance.
   224  	key           An alphanumeric key.
   225  	
   226  	POSTs will be logged as a Kafka JSON message with the following format:
   227  	{ 
   228  		"Action": "postkv",
   229  		"Key": <key>,
   230  		"Bytes": <number of bytes in data>,
   231  		"UUID": <UUID on which POST was done>
   232  	}
   233  
   234  GET <api URL>/node/<UUID>/<data name>/keyvalues[?jsontar=true]
   235  POST <api URL>/node/<UUID>/<data name>/keyvalues
   236  
   237  	Allows batch query or ingest of data. 
   238  
   239  	KeyValue data needs to be serialized in a format defined by the following protobuf3 definitions:
   240  
   241  		message KeyValue {
   242  			string key = 1;
   243  			bytes value = 2;
   244  		}
   245  
   246  		message Keys {
   247  			repeated string keys = 1;
   248  		}
   249  		
   250  		message KeyValues {
   251  			repeated KeyValue kvs = 1;
   252  		}
   253  	
   254  	For GET, the query body must include a Keys serialization and a KeyValues serialization is
   255  	returned.  If a key is not found, it is included in return but with nil value (0 bytes or "{}").
   256  
   257  	For POST, the query body must include a KeyValues serialization.
   258  	
   259  	POSTs will be logged as a series of Kafka JSON messages, each with the format equivalent
   260  	to the single POST /key:
   261  	{ 
   262  		"Action": "postkv",
   263  		"Key": <key>,
   264  		"Bytes": <number of bytes in data>,
   265  		"UUID": <UUID on which POST was done>
   266  	}
   267  
   268  	Arguments:
   269  
   270  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   271  	data name     Name of keyvalue data instance.
   272  
   273  	GET Query-string Options (only one of these allowed):
   274  
   275  	json        If set true, returns JSON (see below) where values must be valid JSON.
   276  	jsontar		If set to any value for GET, query body must be JSON array of string keys
   277  				and the returned data will be a tarfile with keys as file names.
   278  
   279  	Response types:
   280  
   281  	1) json (values are expected to be valid JSON or an error is returned)
   282  
   283  		{
   284  			"key1": value1,
   285  			"key2": value2,
   286  			...
   287  			"bad key": {}
   288  		}
   289  
   290  		If key wasn't found, the "{}" value is returned.
   291  
   292  	2) tar
   293  
   294  		A tarfile is returned with each keys specifying the file names and
   295  		values as the file bytes.
   296  
   297  	3) protobuf3
   298  	
   299  		KeyValue data needs to be serialized in a format defined by the following 
   300  		protobuf3 definitions:
   301  
   302  		message KeyValue {
   303  			string key = 1;
   304  			bytes value = 2;
   305  		}
   306  
   307  		message KeyValues {
   308  			repeated KeyValue kvs = 1;
   309  		}
   310  
   311  	Arguments:
   312  
   313  	UUID          Hexadecimal string with enough characters to uniquely identify a version node.
   314  	data name     Name of keyvalue data instance.
   315  	key1          Lexicographically lowest alphanumeric key in range.
   316  	key2          Lexicographically highest alphanumeric key in range.
   317  
   318  	GET Query-string Options (only one of these allowed):
   319  
   320  	json        If set to "true", the response will be JSON as above and the values must
   321  					be valid JSON or an error will be returned.
   322  	tar			If set to "true", the response will be a tarfile with keys as file names.
   323  	protobuf	If set to "true", the response will be protobuf KeyValues response
   324  
   325  	check		If json=true, setting check=false will tell server to trust that the
   326  					values will be valid JSON instead of parsing it as a check.
   327  `
   328  
   329  func init() {
   330  	datastore.Register(NewType())
   331  
   332  	// Need to register types that will be used to fulfill interfaces.
   333  	gob.Register(&Type{})
   334  	gob.Register(&Data{})
   335  }
   336  
   337  // Type embeds the datastore's Type to create a unique type for keyvalue functions.
   338  type Type struct {
   339  	datastore.Type
   340  }
   341  
   342  // NewType returns a pointer to a new keyvalue Type with default values set.
   343  func NewType() *Type {
   344  	dtype := new(Type)
   345  	dtype.Type = datastore.Type{
   346  		Name:    TypeName,
   347  		URL:     RepoURL,
   348  		Version: Version,
   349  		Requirements: &storage.Requirements{
   350  			Batcher: true,
   351  		},
   352  	}
   353  	return dtype
   354  }
   355  
   356  // --- TypeService interface ---
   357  
   358  // NewDataService returns a pointer to new keyvalue data with default values.
   359  func (dtype *Type) NewDataService(uuid dvid.UUID, id dvid.InstanceID, name dvid.InstanceName, c dvid.Config) (datastore.DataService, error) {
   360  	basedata, err := datastore.NewDataService(dtype, uuid, id, name, c)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	return &Data{basedata}, nil
   365  }
   366  
   367  func (dtype *Type) Help() string {
   368  	return fmt.Sprintf(helpMessage)
   369  }
   370  
   371  // GetByUUIDName returns a pointer to labelblk data given a UUID and data name.
   372  func GetByUUIDName(uuid dvid.UUID, name dvid.InstanceName) (*Data, error) {
   373  	source, err := datastore.GetDataByUUIDName(uuid, name)
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	data, ok := source.(*Data)
   378  	if !ok {
   379  		return nil, fmt.Errorf("Instance '%s' is not a keyvalue datatype!", name)
   380  	}
   381  	return data, nil
   382  }
   383  
   384  // Data embeds the datastore's Data and extends it with keyvalue properties (none for now).
   385  type Data struct {
   386  	*datastore.Data
   387  }
   388  
   389  func (d *Data) Equals(d2 *Data) bool {
   390  	return d.Data.Equals(d2.Data)
   391  }
   392  
   393  func (d *Data) MarshalJSON() ([]byte, error) {
   394  	return json.Marshal(struct {
   395  		Base     *datastore.Data
   396  		Extended struct{}
   397  	}{
   398  		d.Data,
   399  		struct{}{},
   400  	})
   401  }
   402  
   403  func (d *Data) GobDecode(b []byte) error {
   404  	buf := bytes.NewBuffer(b)
   405  	dec := gob.NewDecoder(buf)
   406  	if err := dec.Decode(&(d.Data)); err != nil {
   407  		return err
   408  	}
   409  	return nil
   410  }
   411  
   412  func (d *Data) GobEncode() ([]byte, error) {
   413  	var buf bytes.Buffer
   414  	enc := gob.NewEncoder(&buf)
   415  	if err := enc.Encode(d.Data); err != nil {
   416  		return nil, err
   417  	}
   418  	return buf.Bytes(), nil
   419  }
   420  
   421  // KeyExists returns true if a key is found.
   422  func (d *Data) KeyExists(ctx storage.Context, keyStr string) (bool, error) {
   423  	db, err := datastore.GetKeyValueDB(d)
   424  	if err != nil {
   425  		return false, err
   426  	}
   427  	tk, err := NewTKey(keyStr)
   428  	if err != nil {
   429  		return false, err
   430  	}
   431  	return db.Exists(ctx, tk)
   432  }
   433  
   434  func (d *Data) GetKeysInRange(ctx storage.Context, keyBeg, keyEnd string) ([]string, error) {
   435  	db, err := datastore.GetOrderedKeyValueDB(d)
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  
   440  	// Compute first and last key for range
   441  	first, err := NewTKey(keyBeg)
   442  	if err != nil {
   443  		return nil, err
   444  	}
   445  	last, err := NewTKey(keyEnd)
   446  	if err != nil {
   447  		return nil, err
   448  	}
   449  	keys, err := db.KeysInRange(ctx, first, last)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  	keyList := []string{}
   454  	for _, key := range keys {
   455  		keyStr, err := DecodeTKey(key)
   456  		if err != nil {
   457  			return nil, err
   458  		}
   459  		keyList = append(keyList, keyStr)
   460  	}
   461  	return keyList, nil
   462  }
   463  
   464  func (d *Data) GetKeys(ctx storage.Context) ([]string, error) {
   465  	db, err := datastore.GetOrderedKeyValueDB(d)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  
   470  	// Compute first and last key for range
   471  	first := storage.MinTKey(keyStandard)
   472  	last := storage.MaxTKey(keyStandard)
   473  	keys, err := db.KeysInRange(ctx, first, last)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  	keyList := []string{}
   478  	for _, key := range keys {
   479  		keyStr, err := DecodeTKey(key)
   480  		if err != nil {
   481  			return nil, err
   482  		}
   483  		keyList = append(keyList, keyStr)
   484  	}
   485  	return keyList, nil
   486  }
   487  
   488  // GetData gets a value using a key
   489  func (d *Data) GetData(ctx storage.Context, keyStr string) ([]byte, bool, error) {
   490  	db, err := datastore.GetOrderedKeyValueDB(d)
   491  	if err != nil {
   492  		return nil, false, err
   493  	}
   494  	tk, err := NewTKey(keyStr)
   495  	if err != nil {
   496  		return nil, false, err
   497  	}
   498  	data, err := db.Get(ctx, tk)
   499  	if err != nil {
   500  		return nil, false, fmt.Errorf("Error in retrieving key '%s': %v", keyStr, err)
   501  	}
   502  	if data == nil {
   503  		return nil, false, nil
   504  	}
   505  	uncompress := true
   506  	value, _, err := dvid.DeserializeData(data, uncompress)
   507  	if err != nil {
   508  		return nil, false, fmt.Errorf("Unable to deserialize data for key '%s': %v\n", keyStr, err)
   509  	}
   510  	return value, true, nil
   511  }
   512  
   513  // PutData puts a key-value at a given uuid
   514  func (d *Data) PutData(ctx storage.Context, keyStr string, value []byte) error {
   515  	db, err := datastore.GetOrderedKeyValueDB(d)
   516  	if err != nil {
   517  		return err
   518  	}
   519  	serialization, err := dvid.SerializeData(value, d.Compression(), d.Checksum())
   520  	if err != nil {
   521  		return fmt.Errorf("Unable to serialize data: %v\n", err)
   522  	}
   523  	tk, err := NewTKey(keyStr)
   524  	if err != nil {
   525  		return err
   526  	}
   527  	return db.Put(ctx, tk, serialization)
   528  }
   529  
   530  // DeleteData deletes a key-value pair
   531  func (d *Data) DeleteData(ctx storage.Context, keyStr string) error {
   532  	db, err := datastore.GetOrderedKeyValueDB(d)
   533  	if err != nil {
   534  		return err
   535  	}
   536  	tk, err := NewTKey(keyStr)
   537  	if err != nil {
   538  		return err
   539  	}
   540  	return db.Delete(ctx, tk)
   541  }
   542  
   543  // put handles a PUT command-line request.
   544  func (d *Data) put(cmd datastore.Request, reply *datastore.Response) error {
   545  	if len(cmd.Command) < 5 {
   546  		return fmt.Errorf("The key name must be specified after 'put'")
   547  	}
   548  	if len(cmd.Input) == 0 {
   549  		return fmt.Errorf("No data was passed into standard input")
   550  	}
   551  	var uuidStr, dataName, cmdStr, keyStr string
   552  	cmd.CommandArgs(1, &uuidStr, &dataName, &cmdStr, &keyStr)
   553  
   554  	_, versionID, err := datastore.MatchingUUID(uuidStr)
   555  	if err != nil {
   556  		return err
   557  	}
   558  
   559  	// Store data
   560  	if !d.Versioned() {
   561  		// Map everything to root version.
   562  		versionID, err = datastore.GetRepoRootVersion(versionID)
   563  		if err != nil {
   564  			return err
   565  		}
   566  	}
   567  	ctx := datastore.NewVersionedCtx(d, versionID)
   568  	if err = d.PutData(ctx, keyStr, cmd.Input); err != nil {
   569  		return fmt.Errorf("Error on put to key %q for keyvalue %q: %v\n", keyStr, d.DataName(), err)
   570  	}
   571  
   572  	reply.Output = []byte(fmt.Sprintf("Put %d bytes into key %q for keyvalue %q, uuid %s\n",
   573  		len(cmd.Input), keyStr, d.DataName(), uuidStr))
   574  	return nil
   575  }
   576  
   577  // JSONString returns the JSON for this Data's configuration
   578  func (d *Data) JSONString() (jsonStr string, err error) {
   579  	m, err := json.Marshal(d)
   580  	if err != nil {
   581  		return "", err
   582  	}
   583  	return string(m), nil
   584  }
   585  
   586  // --- DataService interface ---
   587  
   588  func (d *Data) Help() string {
   589  	return fmt.Sprintf(helpMessage)
   590  }
   591  
   592  // DoRPC acts as a switchboard for RPC commands.
   593  func (d *Data) DoRPC(request datastore.Request, reply *datastore.Response) error {
   594  	switch request.TypeCommand() {
   595  	case "put":
   596  		return d.put(request, reply)
   597  	default:
   598  		return fmt.Errorf("Unknown command.  Data '%s' [%s] does not support '%s' command.",
   599  			d.DataName(), d.TypeName(), request.TypeCommand())
   600  	}
   601  }
   602  
   603  // ServeHTTP handles all incoming HTTP requests for this data.
   604  func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) (activity map[string]interface{}) {
   605  	timedLog := dvid.NewTimeLog()
   606  
   607  	// Break URL request into arguments
   608  	url := r.URL.Path[len(server.WebAPIPath):]
   609  	parts := strings.Split(url, "/")
   610  	if len(parts[len(parts)-1]) == 0 {
   611  		parts = parts[:len(parts)-1]
   612  	}
   613  
   614  	if len(parts) < 4 {
   615  		server.BadRequest(w, r, "incomplete API specification")
   616  		return
   617  	}
   618  
   619  	var comment string
   620  	action := strings.ToLower(r.Method)
   621  
   622  	switch parts[3] {
   623  	case "help":
   624  		w.Header().Set("Content-Type", "text/plain")
   625  		fmt.Fprintln(w, d.Help())
   626  		return
   627  
   628  	case "info":
   629  		jsonStr, err := d.JSONString()
   630  		if err != nil {
   631  			server.BadRequest(w, r, err)
   632  			return
   633  		}
   634  		w.Header().Set("Content-Type", "application/json")
   635  		fmt.Fprintf(w, jsonStr)
   636  		return
   637  
   638  	case "tags":
   639  		if action == "post" {
   640  			replace := r.URL.Query().Get("replace") == "true"
   641  			if err := datastore.SetTagsByJSON(d, uuid, replace, r.Body); err != nil {
   642  				server.BadRequest(w, r, err)
   643  				return
   644  			}
   645  		} else {
   646  			jsonBytes, err := d.MarshalJSONTags()
   647  			if err != nil {
   648  				server.BadRequest(w, r, err)
   649  				return
   650  			}
   651  			w.Header().Set("Content-Type", "application/json")
   652  			fmt.Fprintf(w, string(jsonBytes))
   653  		}
   654  
   655  	case "keys":
   656  		keyList, err := d.GetKeys(ctx)
   657  		if err != nil {
   658  			server.BadRequest(w, r, err)
   659  			return
   660  		}
   661  		jsonBytes, err := json.Marshal(keyList)
   662  		if err != nil {
   663  			server.BadRequest(w, r, err)
   664  			return
   665  		}
   666  		w.Header().Set("Content-Type", "application/json")
   667  		fmt.Fprintf(w, string(jsonBytes))
   668  		comment = "HTTP GET keys"
   669  
   670  	case "keyrange":
   671  		if len(parts) < 6 {
   672  			server.BadRequest(w, r, "expect beginning and end keys to follow 'keyrange' endpoint")
   673  			return
   674  		}
   675  
   676  		// Return JSON list of keys
   677  		keyBeg := parts[4]
   678  		keyEnd := parts[5]
   679  		keyList, err := d.GetKeysInRange(ctx, keyBeg, keyEnd)
   680  		if err != nil {
   681  			server.BadRequest(w, r, err)
   682  			return
   683  		}
   684  		jsonBytes, err := json.Marshal(keyList)
   685  		if err != nil {
   686  			server.BadRequest(w, r, err)
   687  			return
   688  		}
   689  		w.Header().Set("Content-Type", "application/json")
   690  		fmt.Fprintf(w, string(jsonBytes))
   691  		comment = fmt.Sprintf("HTTP GET keyrange [%q, %q]", keyBeg, keyEnd)
   692  
   693  	case "keyrangevalues":
   694  		if len(parts) < 6 {
   695  			server.BadRequest(w, r, "expect beginning and end keys to follow 'keyrangevalues' endpoint")
   696  			return
   697  		}
   698  
   699  		// Return JSON list of keys
   700  		keyBeg := parts[4]
   701  		keyEnd := parts[5]
   702  		w.Header().Set("Content-Type", "application/json")
   703  		numKeys, err := d.sendJSONValuesInRange(w, r, ctx, keyBeg, keyEnd)
   704  		if err != nil {
   705  			server.BadRequest(w, r, err)
   706  			return
   707  		}
   708  		comment = fmt.Sprintf("HTTP GET keyrangevalues sent %d values for [%q, %q]", numKeys, keyBeg, keyEnd)
   709  
   710  	case "keyvalues":
   711  		switch action {
   712  		case "get":
   713  			numKeys, writtenBytes, err := d.handleKeyValues(w, r, uuid, ctx)
   714  			if err != nil {
   715  				server.BadRequest(w, r, "GET /keyvalues on %d keys, data %q: %v", numKeys, d.DataName(), err)
   716  				return
   717  			}
   718  			comment = fmt.Sprintf("HTTP GET keyvalues on %d keys, %d bytes, data %q", numKeys, writtenBytes, d.DataName())
   719  		case "post":
   720  			if err := d.handleIngest(r, uuid, ctx); err != nil {
   721  				server.BadRequest(w, r, err)
   722  				return
   723  			}
   724  			comment = fmt.Sprintf("HTTP POST keyvalues on data %q", d.DataName())
   725  		default:
   726  			server.BadRequest(w, r, "key endpoint does not support %q HTTP verb", action)
   727  			return
   728  		}
   729  
   730  	case "key":
   731  		if len(parts) < 5 {
   732  			server.BadRequest(w, r, "expect key string to follow 'key' endpoint")
   733  			return
   734  		}
   735  		keyStr := parts[4]
   736  
   737  		switch action {
   738  		case "head":
   739  			found, err := d.KeyExists(ctx, keyStr)
   740  			if err != nil {
   741  				server.BadRequest(w, r, err)
   742  				return
   743  			}
   744  			if found {
   745  				w.WriteHeader(http.StatusOK)
   746  			} else {
   747  				w.WriteHeader(http.StatusNotFound)
   748  			}
   749  			return
   750  
   751  		case "get":
   752  			// Return value of single key
   753  			value, found, err := d.GetData(ctx, keyStr)
   754  			if err != nil {
   755  				server.BadRequest(w, r, err)
   756  				return
   757  			}
   758  			if !found {
   759  				http.Error(w, fmt.Sprintf("Key %q not found", keyStr), http.StatusNotFound)
   760  				return
   761  			}
   762  			if value != nil || len(value) > 0 {
   763  				_, err = w.Write(value)
   764  				if err != nil {
   765  					server.BadRequest(w, r, err)
   766  					return
   767  				}
   768  				w.Header().Set("Content-Type", "application/octet-stream")
   769  			}
   770  			comment = fmt.Sprintf("HTTP GET key %q of keyvalue %q: %d bytes (%s)", keyStr, d.DataName(), len(value), url)
   771  
   772  		case "delete":
   773  			if err := d.DeleteData(ctx, keyStr); err != nil {
   774  				server.BadRequest(w, r, err)
   775  				return
   776  			}
   777  			comment = fmt.Sprintf("HTTP DELETE data with key %q of keyvalue %q (%s)", keyStr, d.DataName(), url)
   778  
   779  		case "post":
   780  			data, err := ioutil.ReadAll(r.Body)
   781  			if err != nil {
   782  				server.BadRequest(w, r, err)
   783  				return
   784  			}
   785  
   786  			go func() {
   787  				msginfo := map[string]interface{}{
   788  					"Action":    "postkv",
   789  					"Key":       keyStr,
   790  					"Bytes":     len(data),
   791  					"UUID":      string(uuid),
   792  					"Timestamp": time.Now().String(),
   793  				}
   794  				jsonmsg, _ := json.Marshal(msginfo)
   795  				if err = d.PublishKafkaMsg(jsonmsg); err != nil {
   796  					dvid.Errorf("Error on sending keyvalue POST op to kafka: %v\n", err)
   797  				}
   798  			}()
   799  
   800  			err = d.PutData(ctx, keyStr, data)
   801  			if err != nil {
   802  				server.BadRequest(w, r, err)
   803  				return
   804  			}
   805  			comment = fmt.Sprintf("HTTP POST keyvalue '%s': %d bytes (%s)", d.DataName(), len(data), url)
   806  		default:
   807  			server.BadRequest(w, r, "key endpoint does not support %q HTTP verb", action)
   808  			return
   809  		}
   810  
   811  	default:
   812  		server.BadAPIRequest(w, r, d)
   813  		return
   814  	}
   815  
   816  	timedLog.Infof(comment)
   817  	return
   818  }
   819  
   820  // StreamKV returns a channel immediately and asynchronously sends all key-value data through
   821  // the channel, closing it when all the data has been sent.
   822  func (d *Data) StreamKV(v dvid.VersionID) (chan storage.KeyValue, error) {
   823  	ch := make(chan storage.KeyValue)
   824  
   825  	db, err := datastore.GetOrderedKeyValueDB(d)
   826  	if err != nil {
   827  		return nil, err
   828  	}
   829  	ctx := datastore.NewVersionedCtx(d, v)
   830  	go func(ch chan storage.KeyValue) {
   831  		err := db.ProcessRange(ctx, MinTKey, MaxTKey, &storage.ChunkOp{}, func(c *storage.Chunk) error {
   832  			if c == nil || c.TKeyValue == nil {
   833  				return nil
   834  			}
   835  			kv := c.TKeyValue
   836  			if kv.V == nil {
   837  				return nil
   838  			}
   839  			key, err := DecodeTKey(kv.K)
   840  			if err != nil {
   841  				return err
   842  			}
   843  			uncompress := true
   844  			val, _, err := dvid.DeserializeData(kv.V, uncompress)
   845  			if err != nil {
   846  				return fmt.Errorf("unable to deserialize data for key %q: %v", key, err)
   847  			}
   848  			ch <- storage.KeyValue{
   849  				K: storage.Key(key),
   850  				V: val,
   851  			}
   852  
   853  			return nil
   854  		})
   855  		if err != nil {
   856  			dvid.Errorf("error during streaming of data for keyvalue instance %q: %v\n", d.DataName(), err)
   857  		}
   858  		dvid.Infof("Closing channel for StreamKV on %v\n", ctx)
   859  		close(ch)
   860  	}(ch)
   861  
   862  	return ch, nil
   863  }
   864  
   865  func (d *Data) sendJSONValuesInRange(w http.ResponseWriter, r *http.Request, ctx *datastore.VersionedCtx, keyBeg, keyEnd string) (numKeys int, err error) {
   866  	tarOut := (r.URL.Query().Get("jsontar") == "true") || (r.URL.Query().Get("tar") == "true")
   867  	jsonOut := r.URL.Query().Get("json") == "true"
   868  	checkVal := r.URL.Query().Get("check") == "true"
   869  	if tarOut && jsonOut {
   870  		err = fmt.Errorf("can only specify tar or json output, not both")
   871  		return
   872  	}
   873  	db, err := datastore.GetOrderedKeyValueDB(d)
   874  	if err != nil {
   875  		return 0, err
   876  	}
   877  
   878  	var kvs proto.KeyValues
   879  	var tw *tar.Writer
   880  
   881  	switch {
   882  	case tarOut:
   883  		w.Header().Set("Content-type", "application/tar")
   884  		tw = tar.NewWriter(w)
   885  	case jsonOut:
   886  		w.Header().Set("Content-type", "application/json")
   887  		if _, err = w.Write([]byte("{")); err != nil {
   888  			return
   889  		}
   890  	default:
   891  	}
   892  
   893  	// Compute first and last key for range
   894  	first, err := NewTKey(keyBeg)
   895  	if err != nil {
   896  		return 0, err
   897  	}
   898  	last, err := NewTKey(keyEnd)
   899  	if err != nil {
   900  		return 0, err
   901  	}
   902  
   903  	var wroteVal bool
   904  	err = db.ProcessRange(ctx, first, last, &storage.ChunkOp{}, func(c *storage.Chunk) error {
   905  		if c == nil || c.TKeyValue == nil {
   906  			return nil
   907  		}
   908  		kv := c.TKeyValue
   909  		if kv.V == nil {
   910  			return nil
   911  		}
   912  		key, err := DecodeTKey(kv.K)
   913  		if err != nil {
   914  			return err
   915  		}
   916  		uncompress := true
   917  		val, _, err := dvid.DeserializeData(kv.V, uncompress)
   918  		if err != nil {
   919  			return fmt.Errorf("Unable to deserialize data for key %q: %v\n", key, err)
   920  		}
   921  		switch {
   922  		case tarOut:
   923  			hdr := &tar.Header{
   924  				Name: key,
   925  				Size: int64(len(val)),
   926  				Mode: 0755,
   927  			}
   928  			if err = tw.WriteHeader(hdr); err != nil {
   929  				return err
   930  			}
   931  			if _, err = tw.Write(val); err != nil {
   932  				return err
   933  			}
   934  		case jsonOut:
   935  			if wroteVal {
   936  				if _, err = w.Write([]byte(",")); err != nil {
   937  					return err
   938  				}
   939  			}
   940  			if len(val) == 0 {
   941  				val = []byte("{}")
   942  			} else if checkVal && !json.Valid(val) {
   943  				return fmt.Errorf("bad JSON for key %q", key)
   944  			}
   945  			out := fmt.Sprintf(`"%s":`, key)
   946  			if _, err = w.Write([]byte(out)); err != nil {
   947  				return err
   948  			}
   949  			if _, err = w.Write(val); err != nil {
   950  				return err
   951  			}
   952  			wroteVal = true
   953  		default:
   954  			kv := &proto.KeyValue{
   955  				Key:   key,
   956  				Value: val,
   957  			}
   958  			kvs.Kvs = append(kvs.Kvs, kv)
   959  		}
   960  
   961  		return nil
   962  	})
   963  	switch {
   964  	case tarOut:
   965  		tw.Close()
   966  	case jsonOut:
   967  		if _, err = w.Write([]byte("}")); err != nil {
   968  			return
   969  		}
   970  	default:
   971  		numKeys = len(kvs.Kvs)
   972  		var serialization []byte
   973  		if serialization, err = pb.Marshal(&kvs); err != nil {
   974  			return
   975  		}
   976  		w.Header().Set("Content-type", "application/octet-stream")
   977  		if _, err = w.Write(serialization); err != nil {
   978  			return
   979  		}
   980  	}
   981  	return
   982  }
   983  
   984  func (d *Data) sendJSONKV(w http.ResponseWriter, ctx *datastore.VersionedCtx, keys []string, checkVal bool) (writtenBytes int, err error) {
   985  	w.Header().Set("Content-type", "application/json")
   986  	if writtenBytes, err = w.Write([]byte("{")); err != nil {
   987  		return
   988  	}
   989  	var n int
   990  	var wroteVal bool
   991  	for _, key := range keys {
   992  		if wroteVal {
   993  			if n, err = w.Write([]byte(",")); err != nil {
   994  				return
   995  			}
   996  			writtenBytes += n
   997  		}
   998  		var val []byte
   999  		var found bool
  1000  		if val, found, err = d.GetData(ctx, key); err != nil {
  1001  			return
  1002  		}
  1003  		if !found {
  1004  			val = nil
  1005  		}
  1006  		if len(val) == 0 {
  1007  			val = []byte("{}")
  1008  		} else if checkVal && !json.Valid(val) {
  1009  			err = fmt.Errorf("bad JSON for key %q", key)
  1010  			return
  1011  		}
  1012  		out := fmt.Sprintf(`"%s":`, key)
  1013  		if n, err = w.Write([]byte(out)); err != nil {
  1014  			return
  1015  		}
  1016  		writtenBytes += n
  1017  		if n, err = w.Write(val); err != nil {
  1018  			return
  1019  		}
  1020  		writtenBytes += n
  1021  		wroteVal = true
  1022  	}
  1023  	_, err = w.Write([]byte("}"))
  1024  	return
  1025  }
  1026  
  1027  func (d *Data) sendTarKV(w http.ResponseWriter, ctx *datastore.VersionedCtx, keys []string) (writtenBytes int, err error) {
  1028  	var n int
  1029  	w.Header().Set("Content-type", "application/tar")
  1030  	tw := tar.NewWriter(w)
  1031  	for _, key := range keys {
  1032  		var val []byte
  1033  		var found bool
  1034  		if val, found, err = d.GetData(ctx, key); err != nil {
  1035  			return
  1036  		}
  1037  		if !found {
  1038  			val = nil
  1039  		}
  1040  		hdr := &tar.Header{
  1041  			Name: key,
  1042  			Size: int64(len(val)),
  1043  			Mode: 0755,
  1044  		}
  1045  		if err = tw.WriteHeader(hdr); err != nil {
  1046  			return
  1047  		}
  1048  		if n, err = tw.Write(val); err != nil {
  1049  			return
  1050  		}
  1051  		writtenBytes += n
  1052  	}
  1053  	tw.Close()
  1054  	return
  1055  }
  1056  
  1057  func (d *Data) sendProtobufKV(w http.ResponseWriter, ctx *datastore.VersionedCtx, keys proto.Keys) (writtenBytes int, err error) {
  1058  	var kvs proto.KeyValues
  1059  	kvs.Kvs = make([]*proto.KeyValue, len(keys.Keys))
  1060  	for i, key := range keys.Keys {
  1061  		var val []byte
  1062  		var found bool
  1063  		if val, found, err = d.GetData(ctx, key); err != nil {
  1064  			return
  1065  		}
  1066  		if !found {
  1067  			val = nil
  1068  		}
  1069  		kvs.Kvs[i] = &proto.KeyValue{
  1070  			Key:   key,
  1071  			Value: val,
  1072  		}
  1073  	}
  1074  	var serialization []byte
  1075  	if serialization, err = pb.Marshal(&kvs); err != nil {
  1076  		return
  1077  	}
  1078  	w.Header().Set("Content-type", "application/octet-stream")
  1079  	if writtenBytes, err = w.Write(serialization); err != nil {
  1080  		return
  1081  	}
  1082  	if writtenBytes != len(serialization) {
  1083  		err = fmt.Errorf("unable to write all %d bytes of serialized keyvalues: only %d bytes written", len(serialization), writtenBytes)
  1084  	}
  1085  	return
  1086  }
  1087  
  1088  func (d *Data) handleKeyValues(w http.ResponseWriter, r *http.Request, uuid dvid.UUID, ctx *datastore.VersionedCtx) (numKeys, writtenBytes int, err error) {
  1089  	tarOut := (r.URL.Query().Get("jsontar") == "true") || (r.URL.Query().Get("tar") == "true")
  1090  	jsonOut := r.URL.Query().Get("json") == "true"
  1091  	checkVal := r.URL.Query().Get("check") == "true"
  1092  	if tarOut && jsonOut {
  1093  		err = fmt.Errorf("can only specify tar or json output, not both")
  1094  		return
  1095  	}
  1096  	var data []byte
  1097  	data, err = ioutil.ReadAll(r.Body)
  1098  	if err != nil {
  1099  		return
  1100  	}
  1101  	switch {
  1102  	case tarOut:
  1103  		var keys []string
  1104  		if err = json.Unmarshal(data, &keys); err != nil {
  1105  			return
  1106  		}
  1107  		numKeys = len(keys)
  1108  		writtenBytes, err = d.sendTarKV(w, ctx, keys)
  1109  	case jsonOut:
  1110  		var keys []string
  1111  		if err = json.Unmarshal(data, &keys); err != nil {
  1112  			return
  1113  		}
  1114  		numKeys = len(keys)
  1115  		writtenBytes, err = d.sendJSONKV(w, ctx, keys, checkVal)
  1116  	default:
  1117  		var keys proto.Keys
  1118  		if err = pb.Unmarshal(data, &keys); err != nil {
  1119  			return
  1120  		}
  1121  		numKeys = len(keys.Keys)
  1122  		writtenBytes, err = d.sendProtobufKV(w, ctx, keys)
  1123  	}
  1124  	return
  1125  }
  1126  
  1127  func (d *Data) handleIngest(r *http.Request, uuid dvid.UUID, ctx *datastore.VersionedCtx) error {
  1128  	data, err := ioutil.ReadAll(r.Body)
  1129  	if err != nil {
  1130  		return err
  1131  	}
  1132  	var kvs proto.KeyValues
  1133  	if err := pb.Unmarshal(data, &kvs); err != nil {
  1134  		return err
  1135  	}
  1136  	for _, kv := range kvs.Kvs {
  1137  		err = d.PutData(ctx, kv.Key, kv.Value)
  1138  		if err != nil {
  1139  			return err
  1140  		}
  1141  
  1142  		msginfo := map[string]interface{}{
  1143  			"Action":    "postkv",
  1144  			"Key":       kv.Key,
  1145  			"Bytes":     len(kv.Value),
  1146  			"UUID":      string(uuid),
  1147  			"Timestamp": time.Now().String(),
  1148  		}
  1149  		jsonmsg, _ := json.Marshal(msginfo)
  1150  		if err = d.PublishKafkaMsg(jsonmsg); err != nil {
  1151  			dvid.Errorf("Error on sending keyvalue POST op to kafka: %v\n", err)
  1152  		}
  1153  	}
  1154  	return nil
  1155  }