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 }