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  }