github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/shard_replication.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 db 13 14 import ( 15 "context" 16 "fmt" 17 "sync" 18 19 "github.com/go-openapi/strfmt" 20 "github.com/google/uuid" 21 "github.com/weaviate/weaviate/entities/storobj" 22 "github.com/weaviate/weaviate/usecases/objects" 23 "github.com/weaviate/weaviate/usecases/replica" 24 ) 25 26 type replicaTask func(context.Context) interface{} 27 28 type pendingReplicaTasks struct { 29 sync.Mutex 30 Tasks map[string]replicaTask 31 } 32 33 func (p *pendingReplicaTasks) clear() { 34 p.Lock() 35 // TODO: can we postpone deletion until all pending replications are done 36 p.Tasks = nil 37 p.Unlock() 38 } 39 40 func (p *pendingReplicaTasks) get(requestID string) (replicaTask, bool) { 41 p.Lock() 42 defer p.Unlock() 43 t, ok := p.Tasks[requestID] 44 return t, ok 45 } 46 47 func (p *pendingReplicaTasks) set(requestID string, task replicaTask) { 48 p.Lock() 49 p.Tasks[requestID] = task 50 p.Unlock() 51 } 52 53 func (p *pendingReplicaTasks) delete(requestID string) { 54 p.Lock() 55 delete(p.Tasks, requestID) 56 p.Unlock() 57 } 58 59 func (s *Shard) commitReplication(ctx context.Context, requestID string, backupReadLock *backupMutex) interface{} { 60 f, ok := s.replicationMap.get(requestID) 61 if !ok { 62 return nil 63 } 64 defer s.replicationMap.delete(requestID) 65 backupReadLock.RLock() 66 defer backupReadLock.RUnlock() 67 68 return f(ctx) 69 } 70 71 func (s *Shard) abortReplication(ctx context.Context, requestID string) replica.SimpleResponse { 72 s.replicationMap.delete(requestID) 73 return replica.SimpleResponse{} 74 } 75 76 func (s *Shard) preparePutObject(ctx context.Context, requestID string, object *storobj.Object) replica.SimpleResponse { 77 uuid, err := parseBytesUUID(object.ID()) 78 if err != nil { 79 return replica.SimpleResponse{Errors: []replica.Error{{ 80 Code: replica.StatusPreconditionFailed, Msg: err.Error(), 81 }}} 82 } 83 task := func(ctx context.Context) interface{} { 84 resp := replica.SimpleResponse{} 85 if err := s.putOne(ctx, uuid, object); err != nil { 86 resp.Errors = []replica.Error{ 87 {Code: replica.StatusConflict, Msg: err.Error()}, 88 } 89 } 90 return resp 91 } 92 s.replicationMap.set(requestID, task) 93 return replica.SimpleResponse{} 94 } 95 96 func (s *Shard) prepareMergeObject(ctx context.Context, requestID string, doc *objects.MergeDocument) replica.SimpleResponse { 97 uuid, err := parseBytesUUID(doc.ID) 98 if err != nil { 99 return replica.SimpleResponse{Errors: []replica.Error{ 100 {Code: replica.StatusPreconditionFailed, Msg: err.Error()}, 101 }} 102 } 103 task := func(ctx context.Context) interface{} { 104 resp := replica.SimpleResponse{} 105 if err := s.merge(ctx, uuid, *doc); err != nil { 106 resp.Errors = []replica.Error{ 107 {Code: replica.StatusConflict, Msg: err.Error()}, 108 } 109 } 110 return resp 111 } 112 s.replicationMap.set(requestID, task) 113 return replica.SimpleResponse{} 114 } 115 116 func (s *Shard) prepareDeleteObject(ctx context.Context, requestID string, uuid strfmt.UUID) replica.SimpleResponse { 117 bucket, obj, idBytes, docID, err := s.canDeleteOne(ctx, uuid) 118 if err != nil { 119 return replica.SimpleResponse{ 120 Errors: []replica.Error{ 121 {Code: replica.StatusPreconditionFailed, Msg: err.Error()}, 122 }, 123 } 124 } 125 task := func(ctx context.Context) interface{} { 126 resp := replica.SimpleResponse{} 127 if err := s.deleteOne(ctx, bucket, obj, idBytes, docID); err != nil { 128 resp.Errors = []replica.Error{ 129 {Code: replica.StatusConflict, Msg: err.Error()}, 130 } 131 } 132 return resp 133 } 134 s.replicationMap.set(requestID, task) 135 return replica.SimpleResponse{} 136 } 137 138 func (s *Shard) preparePutObjects(ctx context.Context, requestID string, objects []*storobj.Object) replica.SimpleResponse { 139 task := func(ctx context.Context) interface{} { 140 rawErrs := s.putBatch(ctx, objects) 141 resp := replica.SimpleResponse{Errors: make([]replica.Error, len(rawErrs))} 142 for i, err := range rawErrs { 143 if err != nil { 144 resp.Errors[i] = replica.Error{Code: replica.StatusConflict, Msg: err.Error()} 145 } 146 } 147 return resp 148 } 149 s.replicationMap.set(requestID, task) 150 return replica.SimpleResponse{} 151 } 152 153 func (s *Shard) prepareDeleteObjects(ctx context.Context, requestID string, uuids []strfmt.UUID, dryRun bool) replica.SimpleResponse { 154 task := func(ctx context.Context) interface{} { 155 result := newDeleteObjectsBatcher(s).Delete(ctx, uuids, dryRun) 156 resp := replica.DeleteBatchResponse{ 157 Batch: make([]replica.UUID2Error, len(result)), 158 } 159 160 for i, r := range result { 161 entry := replica.UUID2Error{UUID: string(r.UUID)} 162 if err := r.Err; err != nil { 163 entry.Error = replica.Error{Code: replica.StatusConflict, Msg: err.Error()} 164 } 165 resp.Batch[i] = entry 166 } 167 return resp 168 } 169 s.replicationMap.set(requestID, task) 170 return replica.SimpleResponse{} 171 } 172 173 func (s *Shard) prepareAddReferences(ctx context.Context, requestID string, refs []objects.BatchReference) replica.SimpleResponse { 174 task := func(ctx context.Context) interface{} { 175 rawErrs := newReferencesBatcher(s).References(ctx, refs) 176 resp := replica.SimpleResponse{Errors: make([]replica.Error, len(rawErrs))} 177 for i, err := range rawErrs { 178 if err != nil { 179 resp.Errors[i] = replica.Error{Code: replica.StatusConflict, Msg: err.Error()} 180 } 181 } 182 return resp 183 } 184 s.replicationMap.set(requestID, task) 185 return replica.SimpleResponse{} 186 } 187 188 func parseBytesUUID(id strfmt.UUID) ([]byte, error) { 189 uuid, err := uuid.Parse(string(id)) 190 if err != nil { 191 return nil, fmt.Errorf("parse uuid %q: %w", id, err) 192 } 193 return uuid[:], nil 194 }