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  }