kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/storage/keyvalue/keyvalue.go (about) 1 /* 2 * Copyright 2014 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package keyvalue implements a generic GraphStore for anything that implements 18 // the DB interface. 19 package keyvalue // import "kythe.io/kythe/go/storage/keyvalue" 20 21 import ( 22 "bytes" 23 "context" 24 "errors" 25 "fmt" 26 "io" 27 "strings" 28 "sync" 29 30 "kythe.io/kythe/go/services/graphstore" 31 "kythe.io/kythe/go/util/datasize" 32 "kythe.io/kythe/go/util/log" 33 34 spb "kythe.io/kythe/proto/storage_go_proto" 35 ) 36 37 // debug controls whether debugging information is emitted 38 const debug = false 39 40 // A Store implements the graphstore.Service interface for a keyvalue DB 41 type Store struct { 42 db DB 43 44 shardMu sync.Mutex // guards shardTables/shardSnapshots during construction 45 shardTables map[int64][]shard 46 shardSnapshots map[int64]Snapshot 47 } 48 49 // Range is section of contiguous keys, including Start and excluding End. 50 type Range struct { 51 Start, End []byte 52 } 53 54 // KeyRange returns a Range that contains only the given key. 55 func KeyRange(k []byte) *Range { 56 return &Range{ 57 Start: k, 58 End: append(k[0:len(k):len(k)], 0), 59 } 60 } 61 62 type shard struct { 63 Range 64 count int64 65 } 66 67 // NewGraphStore returns a graphstore.Service backed by the given keyvalue DB. 68 func NewGraphStore(db DB) *Store { 69 return &Store{db: db} 70 } 71 72 // A DB is a sorted key-value store with read/write access. DBs must be Closed 73 // when no longer used to ensure resources are not leaked. 74 type DB interface { 75 // Get returns the value associated with the given key. An io.EOF will be 76 // returned if the key is not found. 77 Get(context.Context, []byte, *Options) ([]byte, error) 78 79 // ScanPrefix returns an Iterator for all key-values starting with the given 80 // key prefix. Options may be nil to use the defaults. 81 ScanPrefix(context.Context, []byte, *Options) (Iterator, error) 82 83 // ScanRange returns an Iterator for all key-values starting with the given 84 // key range. Options may be nil to use the defaults. 85 ScanRange(context.Context, *Range, *Options) (Iterator, error) 86 87 // Writer return a new write-access object 88 Writer(context.Context) (Writer, error) 89 90 // NewSnapshot returns a new consistent view of the DB that can be passed as 91 // an option to DB scan methods. 92 NewSnapshot(context.Context) Snapshot 93 94 // Close release the underlying resources for the database. 95 Close(context.Context) error 96 } 97 98 // Snapshot is a consistent view of the DB. 99 type Snapshot io.Closer 100 101 // Options alters the behavior of an Iterator. 102 type Options struct { 103 // LargeRead expresses the client's intent that the read will likely be 104 // "large" and the implementation should usually avoid certain behaviors such 105 // as caching the entire visited key-value range. Defaults to false. 106 LargeRead bool 107 108 // Snapshot causes the iterator to view the DB as it was at the Snapshot's 109 // creation. 110 Snapshot 111 } 112 113 // IsLargeRead returns the LargeRead option or the default of false when o==nil. 114 func (o *Options) IsLargeRead() bool { 115 return o != nil && o.LargeRead 116 } 117 118 // GetSnapshot returns the Snapshot option or the default of nil when o==nil. 119 func (o *Options) GetSnapshot() Snapshot { 120 if o == nil { 121 return nil 122 } 123 return o.Snapshot 124 } 125 126 // Iterator provides sequential access to a DB. Iterators must be Closed when 127 // no longer used to ensure that resources are not leaked. 128 type Iterator interface { 129 io.Closer 130 131 // Next returns the currently positioned key-value entry and moves to the next 132 // entry. If there is no key-value entry to return, an io.EOF error is 133 // returned. 134 Next() (key, val []byte, err error) 135 136 // Seeks positions the Iterator to the given key. The key must be further 137 // than the current Iterator's position. If the key does not exist, the 138 // Iterator is positioned at the next existing key. If no such key exists, 139 // io.EOF is returned. 140 Seek(key []byte) error 141 } 142 143 // Writer provides write access to a DB. Writes must be Closed when no longer 144 // used to ensure that resources are not leaked. 145 type Writer interface { 146 io.Closer 147 148 // Write writes a key-value entry to the DB. Writes may be batched until the 149 // Writer is Closed. 150 Write(key, val []byte) error 151 } 152 153 // WritePool is a wrapper around a DB that automatically creates and flushes 154 // Writers as data size is written, creating a simple buffered interface for 155 // writing to a DB. This interface is not thread-safe. 156 type WritePool struct { 157 db DB 158 opts *PoolOptions 159 160 wr Writer 161 writes int 162 size uint64 163 } 164 165 // PoolOptions is a set of options used by WritePools. 166 type PoolOptions struct { 167 // MaxWrites is the number of calls to Write before the WritePool 168 // automatically flushes the underlying Writer. This defaults to 32000 169 // writes. 170 MaxWrites int 171 172 // MaxSize is the total size of the keys and values given to Write before the 173 // WritePool automatically flushes the underlying Writer. This defaults to 174 // 32MiB. 175 MaxSize datasize.Size 176 } 177 178 func (o *PoolOptions) maxWrites() int { 179 if o == nil || o.MaxWrites <= 0 { 180 return 32000 181 } 182 return o.MaxWrites 183 } 184 185 func (o *PoolOptions) maxSize() uint64 { 186 if o == nil || o.MaxSize <= 0 { 187 return (datasize.Mebibyte * 32).Bytes() 188 } 189 return o.MaxSize.Bytes() 190 } 191 192 // NewPool returns a new WritePool for the given DB. If opts==nil, its defaults 193 // are used. 194 func NewPool(db DB, opts *PoolOptions) *WritePool { return &WritePool{db: db, opts: opts} } 195 196 // Write buffers the given write until the pool becomes to large or Flush is 197 // called. 198 func (p *WritePool) Write(ctx context.Context, key, val []byte) error { 199 if p.wr == nil { 200 wr, err := p.db.Writer(ctx) 201 if err != nil { 202 return err 203 } 204 p.wr = wr 205 } 206 if err := p.wr.Write(key, val); err != nil { 207 return err 208 } 209 p.size += uint64(len(key)) + uint64(len(val)) 210 p.writes++ 211 if p.opts.maxWrites() <= p.writes || p.opts.maxSize() <= p.size { 212 return p.Flush() 213 } 214 return nil 215 } 216 217 // Flush ensures that all buffered writes are applied to the underlying DB. 218 func (p *WritePool) Flush() error { 219 if p.wr == nil { 220 return nil 221 } 222 if debug { 223 log.Infof("Flushing (%d) %s", p.writes, datasize.Size(p.size)) 224 } 225 err := p.wr.Close() 226 p.wr = nil 227 p.size, p.writes = 0, 0 228 return err 229 } 230 231 // Read implements part of the graphstore.Service interface. 232 func (s *Store) Read(ctx context.Context, req *spb.ReadRequest, f graphstore.EntryFunc) error { 233 keyPrefix, err := KeyPrefix(req.Source, req.EdgeKind) 234 if err != nil { 235 return fmt.Errorf("invalid ReadRequest: %v", err) 236 } 237 iter, err := s.db.ScanPrefix(ctx, keyPrefix, nil) 238 if err != nil { 239 return fmt.Errorf("db seek error: %v", err) 240 } 241 return streamEntries(iter, f) 242 } 243 244 func streamEntries(iter Iterator, f graphstore.EntryFunc) error { 245 defer iter.Close() 246 for { 247 key, val, err := iter.Next() 248 if err == io.EOF { 249 break 250 } else if err != nil { 251 return fmt.Errorf("db iteration error: %v", err) 252 } 253 254 entry, err := Entry(key, val) 255 if err != nil { 256 return fmt.Errorf("encoding error: %v", err) 257 } 258 if err := f(entry); err == io.EOF { 259 return nil 260 } else if err != nil { 261 return err 262 } 263 } 264 return nil 265 } 266 267 // Write implements part of the GraphStore interface. 268 func (s *Store) Write(ctx context.Context, req *spb.WriteRequest) (err error) { 269 // TODO(schroederc): fix shardTables to include new entries 270 271 wr, err := s.db.Writer(ctx) 272 if err != nil { 273 return fmt.Errorf("db writer error: %v", err) 274 } 275 defer func() { 276 cErr := wr.Close() 277 if err == nil && cErr != nil { 278 err = fmt.Errorf("db writer close error: %v", cErr) 279 } 280 }() 281 for _, update := range req.Update { 282 if update.FactName == "" { 283 return errors.New("invalid WriteRequest: Update missing FactName") 284 } 285 updateKey, err := EncodeKey(req.Source, update.FactName, update.EdgeKind, update.Target) 286 if err != nil { 287 return fmt.Errorf("encoding error: %v", err) 288 } 289 if err := wr.Write(updateKey, update.FactValue); err != nil { 290 return fmt.Errorf("db write error: %v", err) 291 } 292 } 293 return nil 294 } 295 296 // Scan implements part of the graphstore.Service interface. 297 func (s *Store) Scan(ctx context.Context, req *spb.ScanRequest, f graphstore.EntryFunc) error { 298 iter, err := s.db.ScanPrefix(ctx, entryKeyPrefixBytes, &Options{LargeRead: true}) 299 if err != nil { 300 return fmt.Errorf("db seek error: %v", err) 301 } 302 defer iter.Close() 303 for { 304 key, val, err := iter.Next() 305 if err == io.EOF { 306 break 307 } else if err != nil { 308 return fmt.Errorf("db iteration error: %v", err) 309 } 310 entry, err := Entry(key, val) 311 if err != nil { 312 return fmt.Errorf("invalid key/value entry: %v", err) 313 } 314 if !graphstore.EntryMatchesScan(req, entry) { 315 continue 316 } else if err := f(entry); err == io.EOF { 317 return nil 318 } else if err != nil { 319 return err 320 } 321 } 322 return nil 323 } 324 325 // Close implements part of the graphstore.Service interface. 326 func (s *Store) Close(ctx context.Context) error { return s.db.Close(ctx) } 327 328 // Count implements part of the graphstore.Sharded interface. 329 func (s *Store) Count(ctx context.Context, req *spb.CountRequest) (int64, error) { 330 if req.Shards < 1 { 331 return 0, fmt.Errorf("invalid number of shards: %d", req.Shards) 332 } else if req.Index < 0 || req.Index >= req.Shards { 333 return 0, fmt.Errorf("invalid index for %d shards: %d", req.Shards, req.Index) 334 } 335 336 tbl, _, err := s.constructShards(ctx, req.Shards) 337 if err != nil { 338 return 0, err 339 } 340 return tbl[req.Index].count, nil 341 } 342 343 // Shard implements part of the graphstore.Sharded interface. 344 func (s *Store) Shard(ctx context.Context, req *spb.ShardRequest, f graphstore.EntryFunc) error { 345 if req.Shards < 1 { 346 return fmt.Errorf("invalid number of shards: %d", req.Shards) 347 } else if req.Index < 0 || req.Index >= req.Shards { 348 return fmt.Errorf("invalid index for %d shards: %d", req.Shards, req.Index) 349 } 350 351 tbl, snapshot, err := s.constructShards(ctx, req.Shards) 352 if err != nil { 353 return err 354 } 355 if tbl[req.Index].count == 0 { 356 return nil 357 } 358 shard := tbl[req.Index] 359 iter, err := s.db.ScanRange(ctx, &shard.Range, &Options{ 360 LargeRead: true, 361 Snapshot: snapshot, 362 }) 363 if err != nil { 364 return err 365 } 366 return streamEntries(iter, f) 367 } 368 369 func (s *Store) constructShards(ctx context.Context, num int64) ([]shard, Snapshot, error) { 370 s.shardMu.Lock() 371 defer s.shardMu.Unlock() 372 if s.shardTables == nil { 373 s.shardTables = make(map[int64][]shard) 374 s.shardSnapshots = make(map[int64]Snapshot) 375 } 376 if tbl, ok := s.shardTables[num]; ok { 377 return tbl, s.shardSnapshots[num], nil 378 } 379 snapshot := s.db.NewSnapshot(ctx) 380 iters := make([]Iterator, num) 381 for i := range iters { 382 var err error 383 iters[i], err = s.db.ScanPrefix(ctx, entryKeyPrefixBytes, &Options{ 384 LargeRead: true, 385 Snapshot: snapshot, 386 }) 387 if err != nil { 388 snapshot.Close() 389 return nil, nil, fmt.Errorf("error creating iterator: %v", err) 390 } 391 } 392 393 // This loop determines the ending key to each shard's range and the number of 394 // entries each iterator has passed. Each iterator always represents the 395 // current ending key to each shard and is moved in (i+1) groups of entries 396 // where i is its index in iters/tbl. If a group consisted of a single entry, 397 // this staggered iteration evenly distribute the iterators across the entire 398 // GraphStore. However, this loop iterates past groups of entries sharing the 399 // same (source+edgeKind) at a time to ensure the property that no node/edge 400 // crosses a shard boundary. This also means that the shards will be less 401 // evenly distributed. 402 tbl := make([]shard, num) 403 loop: 404 for { // Until an iterator (usually iters[num-1]) reaches io.EOF 405 for i, iter := range iters { 406 // Move iters[i] past i+1 sets of entries sharing the same 407 // (source+edgeKind) prefix. 408 for j := 0; j <= i; j++ { 409 k, _, err := iter.Next() 410 if err == io.EOF { 411 break loop 412 } else if err != nil { 413 snapshot.Close() 414 return nil, nil, err 415 } 416 prefix := sourceKindPrefix(k) 417 tbl[i].count++ 418 tbl[i].End = k 419 420 // Iterate past all entries with the same source+kind prefix as k 421 for { 422 k, _, err = iter.Next() 423 if err == io.EOF { 424 break loop 425 } else if err != nil { 426 snapshot.Close() 427 return nil, nil, err 428 } 429 tbl[i].count++ 430 tbl[i].End = k 431 if !bytes.HasPrefix(k, prefix) { 432 break 433 } 434 } 435 } 436 } 437 } 438 439 // Fix up the border shards 440 tbl[0].Start = entryKeyPrefixBytes 441 tbl[num-1].End = entryKeyPrefixEndRange 442 tbl[0].count-- 443 tbl[num-1].count++ 444 445 // Set the starting keys to each shard. 446 for i := int64(1); i < num; i++ { 447 tbl[i].Start = tbl[i-1].End 448 } 449 // Determine the size of each shard. 450 for i := num - 1; i > 0; i-- { 451 tbl[i].count -= tbl[i-1].count 452 } 453 454 s.shardTables[num] = tbl 455 s.shardSnapshots[num] = snapshot 456 return tbl, snapshot, nil 457 } 458 459 func sourceKindPrefix(key []byte) []byte { 460 idx := bytes.IndexRune(key, entryKeySep) 461 return key[:bytes.IndexRune(key[idx+1:], entryKeySep)+idx+2] 462 } 463 464 // GraphStore Implementation Details: 465 // These details are strictly for this particular implementation of a 466 // GraphStore and are *not* specified in the GraphStore specification. These 467 // particular encodings, however, do satisfy the GraphStore requirements, 468 // including the GraphStore entry ordering property. Also, due to the 469 // "entry:" key prefix, this implementation allows for additional embedded 470 // GraphStore metadata/indices using other distinct key prefixes. 471 // 472 // The encoding format for entries in a keyvalue GraphStore is: 473 // "entry:<source>_<edgeKind>_<factName>_<target>" == "<factValue>" 474 // where: 475 // "entry:" == entryKeyPrefix 476 // "_" == entryKeySep 477 // <source> and <target> are the Entry's encoded VNames: 478 // 479 // The encoding format for VNames is: 480 // <signature>-<corpus>-<root>-<path>-<language> 481 // where: 482 // "-" == vNameFieldSep 483 484 const ( 485 entryKeyPrefix = "entry:" 486 487 // entryKeySep is used to separate the source, factName, edgeKind, and target of an 488 // encoded Entry key 489 entryKeySep = '\n' 490 entryKeySepStr = string(entryKeySep) 491 492 // vNameFieldSep is used to separate the fields of an encoded VName 493 vNameFieldSep = "\000" 494 ) 495 496 var ( 497 entryKeyPrefixBytes = []byte(entryKeyPrefix) 498 entryKeyPrefixEndRange = append([]byte(entryKeyPrefix[:len(entryKeyPrefixBytes)-1]), entryKeyPrefix[len(entryKeyPrefixBytes)-1]+1) 499 entryKeySepBytes = []byte{entryKeySep} 500 ) 501 502 // EncodeKey returns a canonical encoding of an Entry (minus its value). 503 func EncodeKey(source *spb.VName, factName string, edgeKind string, target *spb.VName) ([]byte, error) { 504 if source == nil { 505 return nil, errors.New("invalid Entry: missing source VName for key encoding") 506 } else if (edgeKind == "" || target == nil) && (edgeKind != "" || target != nil) { 507 return nil, errors.New("invalid Entry: edgeKind and target Ticket must be both non-empty or empty") 508 } else if strings.Contains(edgeKind, entryKeySepStr) { 509 return nil, errors.New("invalid Entry: edgeKind contains key separator") 510 } else if strings.Contains(factName, entryKeySepStr) { 511 return nil, errors.New("invalid Entry: factName contains key separator") 512 } 513 514 keySuffix := []byte(entryKeySepStr + edgeKind + entryKeySepStr + factName + entryKeySepStr) 515 516 srcEncoding, err := encodeVName(source) 517 if err != nil { 518 return nil, fmt.Errorf("error encoding source VName: %v", err) 519 } else if bytes.Contains(srcEncoding, entryKeySepBytes) { 520 return nil, fmt.Errorf("invalid Entry: source VName contains key separator (%q) %v", entryKeySepBytes, source) 521 } 522 targetEncoding, err := encodeVName(target) 523 if err != nil { 524 return nil, fmt.Errorf("error encoding target VName: %v", err) 525 } else if bytes.Contains(targetEncoding, entryKeySepBytes) { 526 return nil, errors.New("invalid Entry: target VName contains key separator") 527 } 528 529 return bytes.Join([][]byte{ 530 entryKeyPrefixBytes, 531 srcEncoding, 532 keySuffix, 533 targetEncoding, 534 }, nil), nil 535 } 536 537 // KeyPrefix returns a prefix to every encoded key for the given source VName and exact 538 // edgeKind. If edgeKind is "*", the prefix will match any edgeKind. 539 func KeyPrefix(source *spb.VName, edgeKind string) ([]byte, error) { 540 if source == nil { 541 return nil, errors.New("missing source VName") 542 } 543 srcEncoding, err := encodeVName(source) 544 if err != nil { 545 return nil, fmt.Errorf("error encoding source VName: %v", err) 546 } 547 548 prefix := bytes.Join([][]byte{entryKeyPrefixBytes, append(srcEncoding, entryKeySep)}, nil) 549 if edgeKind == "*" { 550 return prefix, nil 551 } 552 553 return bytes.Join([][]byte{prefix, append([]byte(edgeKind), entryKeySep)}, nil), nil 554 } 555 556 // Entry decodes the key (assuming it was encoded by EncodeKey) into an Entry 557 // and populates its value field. 558 func Entry(key []byte, val []byte) (*spb.Entry, error) { 559 if !bytes.HasPrefix(key, entryKeyPrefixBytes) { 560 return nil, fmt.Errorf("key is not prefixed with entry prefix %q", entryKeyPrefix) 561 } 562 keyStr := string(bytes.TrimPrefix(key, entryKeyPrefixBytes)) 563 keyParts := strings.SplitN(keyStr, entryKeySepStr, 4) 564 if len(keyParts) != 4 { 565 return nil, fmt.Errorf("invalid key[%d]: %q", len(keyParts), string(key)) 566 } 567 568 srcVName, err := decodeVName(keyParts[0]) 569 if err != nil { 570 return nil, fmt.Errorf("error decoding source VName: %v", err) 571 } 572 targetVName, err := decodeVName(keyParts[3]) 573 if err != nil { 574 return nil, fmt.Errorf("error decoding target VName: %v", err) 575 } 576 577 return &spb.Entry{ 578 Source: srcVName, 579 FactName: keyParts[2], 580 EdgeKind: keyParts[1], 581 Target: targetVName, 582 FactValue: val, 583 }, nil 584 } 585 586 // encodeVName returns a canonical byte array for the given VName. Returns nil if given nil. 587 func encodeVName(v *spb.VName) ([]byte, error) { 588 if v == nil { 589 return nil, nil 590 } else if strings.Contains(v.Signature, vNameFieldSep) || 591 strings.Contains(v.Corpus, vNameFieldSep) || 592 strings.Contains(v.Root, vNameFieldSep) || 593 strings.Contains(v.Path, vNameFieldSep) || 594 strings.Contains(v.Language, vNameFieldSep) { 595 return nil, fmt.Errorf("VName contains invalid rune: %q", vNameFieldSep) 596 } 597 return []byte(strings.Join([]string{ 598 v.Signature, 599 v.Corpus, 600 v.Root, 601 v.Path, 602 v.Language, 603 }, vNameFieldSep)), nil 604 } 605 606 // decodeVName returns the VName coded in the given string. Returns nil, if len(data) == 0. 607 func decodeVName(data string) (*spb.VName, error) { 608 if len(data) == 0 { 609 return nil, nil 610 } 611 parts := strings.SplitN(data, vNameFieldSep, 5) 612 if len(parts) != 5 { 613 return nil, fmt.Errorf("invalid VName encoding: %q", data) 614 } 615 return &spb.VName{ 616 Signature: parts[0], 617 Corpus: parts[1], 618 Root: parts[2], 619 Path: parts[3], 620 Language: parts[4], 621 }, nil 622 }