kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/services/graphstore/graphstore.go (about) 1 /* 2 * Copyright 2015 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 graphstore defines the Service and Sharded interfaces, and provides 18 // some useful utility functions. 19 package graphstore // import "kythe.io/kythe/go/services/graphstore" 20 21 import ( 22 "context" 23 "errors" 24 "strings" 25 26 "kythe.io/kythe/go/util/compare" 27 28 spb "kythe.io/kythe/proto/storage_go_proto" 29 ) 30 31 // An EntryFunc is a callback from the implementation of a Service to deliver 32 // entry messages. If the callback returns an error, the operation stops. If 33 // the error is io.EOF, the operation returns nil; otherwise it returns the 34 // error value from the callback. 35 type EntryFunc func(*spb.Entry) error 36 37 // Service refers to an open Kythe graph store. 38 type Service interface { 39 // Read calls f with each entry having the ReadRequest's given source 40 // VName, subject to the following rules: 41 // 42 // |----------+---------------------------------------------------------| 43 // | EdgeKind | Result | 44 // |----------+---------------------------------------------------------| 45 // | ΓΈ | All entries with kind and target empty (node entries). | 46 // | "*" | All entries (node and edge, regardless of kind/target). | 47 // | "kind" | All edge entries with the given edge kind. | 48 // |----------+---------------------------------------------------------| 49 // 50 // Read returns when there are no more entries to send. The Read operation should be 51 // implemented with time complexity proportional to the size of the return set. 52 Read(ctx context.Context, req *spb.ReadRequest, f EntryFunc) error 53 54 // Scan calls f with each entries having the specified target VName, kind, 55 // and fact label prefix. If a field is empty, any entry value for that 56 // field matches and will be returned. Scan returns when there are no more 57 // entries to send. Scan is similar to Read, but with no time complexity 58 // restrictions. 59 Scan(ctx context.Context, req *spb.ScanRequest, f EntryFunc) error 60 61 // Write atomically inserts or updates a collection of entries into the store. 62 // Each update is a tuple of the form (kind, target, fact, value). For each such 63 // update, an entry (source, kind, target, fact, value) is written into the store, 64 // replacing any existing entry (source, kind, target, fact, value') that may 65 // exist. Note that this operation cannot delete any data from the store; entries are 66 // only ever inserted or updated. Apart from acting atomically, no other constraints 67 // are placed on the implementation. 68 Write(ctx context.Context, req *spb.WriteRequest) error 69 70 // Close and release any underlying resources used by the store. 71 // No operations may be used on the store after this has been called. 72 Close(ctx context.Context) error 73 } 74 75 // Sharded represents a store that can be arbitrarily sharded for parallel 76 // processing. Depending on the implementation, these methods may not return 77 // consistent results when the store is being written to. Shards are indexed 78 // from 0. 79 type Sharded interface { 80 Service 81 82 // Count returns the number of entries in the given shard. 83 Count(ctx context.Context, req *spb.CountRequest) (int64, error) 84 85 // Shard calls f with each entry in the given shard. 86 Shard(ctx context.Context, req *spb.ShardRequest, f EntryFunc) error 87 } 88 89 // EntryMatchesScan reports whether entry belongs in the result set for req. 90 func EntryMatchesScan(req *spb.ScanRequest, entry *spb.Entry) bool { 91 return (req.GetTarget() == nil || compare.VNamesEqual(entry.Target, req.Target)) && 92 (req.EdgeKind == "" || entry.EdgeKind == req.EdgeKind) && 93 strings.HasPrefix(entry.FactName, req.FactPrefix) 94 } 95 96 // BatchWrites returns a channel of WriteRequests for the given entries. 97 // Consecutive entries with the same Source will be collected in the same 98 // WriteRequest, with each request containing up to maxSize updates. 99 func BatchWrites(entries <-chan *spb.Entry, maxSize int) <-chan *spb.WriteRequest { 100 ch := make(chan *spb.WriteRequest) 101 go func() { 102 defer close(ch) 103 var req *spb.WriteRequest 104 for entry := range entries { 105 update := &spb.WriteRequest_Update{ 106 EdgeKind: entry.EdgeKind, 107 Target: entry.Target, 108 FactName: entry.FactName, 109 FactValue: entry.FactValue, 110 } 111 112 if req != nil && (!compare.VNamesEqual(req.Source, entry.Source) || len(req.Update) >= maxSize) { 113 ch <- req 114 req = nil 115 } 116 117 if req == nil { 118 req = &spb.WriteRequest{ 119 Source: entry.Source, 120 Update: []*spb.WriteRequest_Update{update}, 121 } 122 } else { 123 req.Update = append(req.Update, update) 124 } 125 } 126 if req != nil { 127 ch <- req 128 } 129 }() 130 return ch 131 } 132 133 // ValidEntry determines if the given Entry is correctly constructed. 134 func ValidEntry(e *spb.Entry) error { 135 if e.Source == nil { 136 return errors.New("entry missing source") 137 } else if e.FactName == "" { 138 return errors.New("entry missing fact name") 139 } else if IsEdge(e) { 140 if e.Target == nil { 141 return errors.New("edge entry missing target") 142 } 143 } else if e.Target != nil { 144 return errors.New("node fact entry has extraneous target") 145 } 146 return nil 147 } 148 149 // IsNodeFact determines if the Entry is a node fact; implies !IsEdge(e). 150 func IsNodeFact(e *spb.Entry) bool { return e.EdgeKind == "" } 151 152 // IsEdge determines if the Entry describes an edge; implies !IsNodeFact(e). 153 func IsEdge(e *spb.Entry) bool { return e.EdgeKind != "" }