github.com/weaviate/weaviate@v1.24.6/usecases/replica/transport.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package replica 13 14 import ( 15 "context" 16 "fmt" 17 18 "github.com/go-openapi/strfmt" 19 "github.com/weaviate/weaviate/entities/additional" 20 "github.com/weaviate/weaviate/entities/search" 21 "github.com/weaviate/weaviate/entities/storobj" 22 "github.com/weaviate/weaviate/usecases/objects" 23 ) 24 25 const ( 26 // RequestKey is used to marshalling request IDs 27 RequestKey = "request_id" 28 ) 29 30 // Client is used to read and write objects on replicas 31 type Client interface { 32 rClient 33 wClient 34 } 35 36 // StatusCode is communicate the cause of failure during replication 37 type StatusCode int 38 39 const ( 40 StatusOK = 0 41 StatusClassNotFound = iota + 200 42 StatusShardNotFound 43 StatusNotFound 44 StatusAlreadyExisted 45 StatusNotReady 46 StatusConflict = iota + 300 47 StatusPreconditionFailed 48 StatusReadOnly 49 ) 50 51 // Error reports error happening during replication 52 type Error struct { 53 Code StatusCode `json:"code"` 54 Msg string `json:"msg,omitempty"` 55 Err error `json:"-"` 56 } 57 58 // Empty checks whether e is an empty error which equivalent to e == nil 59 func (e *Error) Empty() bool { 60 return e.Code == StatusOK && e.Msg == "" && e.Err == nil 61 } 62 63 // NewError create new replication error 64 func NewError(code StatusCode, msg string) *Error { 65 return &Error{code, msg, nil} 66 } 67 68 func (e *Error) Clone() *Error { 69 return &Error{Code: e.Code, Msg: e.Msg, Err: e.Err} 70 } 71 72 // Unwrap underlying error 73 func (e *Error) Unwrap() error { return e.Err } 74 75 func (e *Error) Error() string { 76 return fmt.Sprintf("%s %q: %v", statusText(e.Code), e.Msg, e.Err) 77 } 78 79 func (e *Error) IsStatusCode(sc StatusCode) bool { 80 return e.Code == sc 81 } 82 83 // statusText returns a text for the status code. It returns the empty 84 // string if the code is unknown. 85 func statusText(code StatusCode) string { 86 switch code { 87 case StatusOK: 88 return "ok" 89 case StatusNotFound: 90 return "not found" 91 case StatusClassNotFound: 92 return "class not found" 93 case StatusShardNotFound: 94 return "shard not found" 95 case StatusConflict: 96 return "conflict" 97 case StatusPreconditionFailed: 98 return "precondition failed" 99 case StatusAlreadyExisted: 100 return "already existed" 101 case StatusNotReady: 102 return "local index not ready" 103 case StatusReadOnly: 104 return "read only" 105 default: 106 return "" 107 } 108 } 109 110 func (e *Error) Timeout() bool { 111 t, ok := e.Err.(interface { 112 Timeout() bool 113 }) 114 return ok && t.Timeout() 115 } 116 117 type SimpleResponse struct { 118 Errors []Error `json:"errors,omitempty"` 119 } 120 121 func (r *SimpleResponse) FirstError() error { 122 for i, err := range r.Errors { 123 if !err.Empty() { 124 return &r.Errors[i] 125 } 126 } 127 return nil 128 } 129 130 // DeleteBatchResponse represents the response returned by DeleteObjects 131 type DeleteBatchResponse struct { 132 Batch []UUID2Error `json:"batch,omitempty"` 133 } 134 135 type UUID2Error struct { 136 UUID string `json:"uuid,omitempty"` 137 Error Error `json:"error,omitempty"` 138 } 139 140 // FirstError returns the first found error 141 func (r *DeleteBatchResponse) FirstError() error { 142 for i, ue := range r.Batch { 143 if !ue.Error.Empty() { 144 return &r.Batch[i].Error 145 } 146 } 147 return nil 148 } 149 150 type RepairResponse struct { 151 ID string // object id 152 Version int64 // sender's current version of the object 153 UpdateTime int64 // sender's current update time 154 Err string 155 Deleted bool 156 } 157 158 func fromReplicas(xs []objects.Replica) []*storobj.Object { 159 rs := make([]*storobj.Object, len(xs)) 160 for i := range xs { 161 rs[i] = xs[i].Object 162 } 163 return rs 164 } 165 166 // wClient is the client used to write to replicas 167 type wClient interface { 168 PutObject(ctx context.Context, host, index, shard, requestID string, 169 obj *storobj.Object) (SimpleResponse, error) 170 DeleteObject(ctx context.Context, host, index, shard, requestID string, 171 id strfmt.UUID) (SimpleResponse, error) 172 PutObjects(ctx context.Context, host, index, shard, requestID string, 173 objs []*storobj.Object) (SimpleResponse, error) 174 MergeObject(ctx context.Context, host, index, shard, requestID string, 175 mergeDoc *objects.MergeDocument) (SimpleResponse, error) 176 DeleteObjects(ctx context.Context, host, index, shard, requestID string, 177 uuids []strfmt.UUID, dryRun bool) (SimpleResponse, error) 178 AddReferences(ctx context.Context, host, index, shard, requestID string, 179 refs []objects.BatchReference) (SimpleResponse, error) 180 Commit(ctx context.Context, host, index, shard, requestID string, resp interface{}) error 181 Abort(ctx context.Context, host, index, shard, requestID string) (SimpleResponse, error) 182 } 183 184 // rClient is the client used to read from remote replicas 185 type rClient interface { 186 // FetchObject fetches one object 187 FetchObject(_ context.Context, host, index, shard string, 188 id strfmt.UUID, props search.SelectProperties, 189 additional additional.Properties) (objects.Replica, error) 190 191 // FetchObjects fetches objects specified in ids list. 192 FetchObjects(_ context.Context, host, index, shard string, 193 ids []strfmt.UUID) ([]objects.Replica, error) 194 195 // OverwriteObjects conditionally updates existing objects. 196 OverwriteObjects(_ context.Context, host, index, shard string, 197 _ []*objects.VObject) ([]RepairResponse, error) 198 199 // DigestObjects finds a list of objects and returns a compact representation 200 // of a list of the objects. This is used by the replicator to optimize the 201 // number of bytes transferred over the network when fetching a replicated 202 // object 203 DigestObjects(ctx context.Context, host, index, shard string, 204 ids []strfmt.UUID) ([]RepairResponse, error) 205 } 206 207 // finderClient extends RClient with consistency checks 208 type finderClient struct { 209 cl rClient 210 } 211 212 // FullRead reads full object 213 func (fc finderClient) FullRead(ctx context.Context, 214 host, index, shard string, 215 id strfmt.UUID, 216 props search.SelectProperties, 217 additional additional.Properties, 218 ) (objects.Replica, error) { 219 return fc.cl.FetchObject(ctx, host, index, shard, id, props, additional) 220 } 221 222 // DigestReads reads digests of all specified objects 223 func (fc finderClient) DigestReads(ctx context.Context, 224 host, index, shard string, 225 ids []strfmt.UUID, 226 ) ([]RepairResponse, error) { 227 n := len(ids) 228 rs, err := fc.cl.DigestObjects(ctx, host, index, shard, ids) 229 if err == nil && len(rs) != n { 230 err = fmt.Errorf("malformed digest read response: length expected %d got %d", n, len(rs)) 231 } 232 return rs, err 233 } 234 235 // FullReads read full objects 236 func (fc finderClient) FullReads(ctx context.Context, 237 host, index, shard string, 238 ids []strfmt.UUID, 239 ) ([]objects.Replica, error) { 240 n := len(ids) 241 rs, err := fc.cl.FetchObjects(ctx, host, index, shard, ids) 242 if m := len(rs); err == nil && n != m { 243 err = fmt.Errorf("malformed full read response: length expected %d got %d", n, m) 244 } 245 return rs, err 246 } 247 248 // Overwrite specified object with most recent contents 249 func (fc finderClient) Overwrite(ctx context.Context, 250 host, index, shard string, 251 xs []*objects.VObject, 252 ) ([]RepairResponse, error) { 253 return fc.cl.OverwriteObjects(ctx, host, index, shard, xs) 254 }