github.com/MetalBlockchain/metalgo@v1.11.9/database/rpcdb/db_client.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package rpcdb
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"sync"
    10  
    11  	"google.golang.org/protobuf/types/known/emptypb"
    12  
    13  	"github.com/MetalBlockchain/metalgo/database"
    14  	"github.com/MetalBlockchain/metalgo/utils"
    15  	"github.com/MetalBlockchain/metalgo/utils/set"
    16  
    17  	rpcdbpb "github.com/MetalBlockchain/metalgo/proto/pb/rpcdb"
    18  )
    19  
    20  var (
    21  	_ database.Database = (*DatabaseClient)(nil)
    22  	_ database.Batch    = (*batch)(nil)
    23  	_ database.Iterator = (*iterator)(nil)
    24  )
    25  
    26  // DatabaseClient is an implementation of database that talks over RPC.
    27  type DatabaseClient struct {
    28  	client rpcdbpb.DatabaseClient
    29  
    30  	closed utils.Atomic[bool]
    31  }
    32  
    33  // NewClient returns a database instance connected to a remote database instance
    34  func NewClient(client rpcdbpb.DatabaseClient) *DatabaseClient {
    35  	return &DatabaseClient{client: client}
    36  }
    37  
    38  // Has attempts to return if the database has a key with the provided value.
    39  func (db *DatabaseClient) Has(key []byte) (bool, error) {
    40  	resp, err := db.client.Has(context.Background(), &rpcdbpb.HasRequest{
    41  		Key: key,
    42  	})
    43  	if err != nil {
    44  		return false, err
    45  	}
    46  	return resp.Has, ErrEnumToError[resp.Err]
    47  }
    48  
    49  // Get attempts to return the value that was mapped to the key that was provided
    50  func (db *DatabaseClient) Get(key []byte) ([]byte, error) {
    51  	resp, err := db.client.Get(context.Background(), &rpcdbpb.GetRequest{
    52  		Key: key,
    53  	})
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	return resp.Value, ErrEnumToError[resp.Err]
    58  }
    59  
    60  // Put attempts to set the value this key maps to
    61  func (db *DatabaseClient) Put(key, value []byte) error {
    62  	resp, err := db.client.Put(context.Background(), &rpcdbpb.PutRequest{
    63  		Key:   key,
    64  		Value: value,
    65  	})
    66  	if err != nil {
    67  		return err
    68  	}
    69  	return ErrEnumToError[resp.Err]
    70  }
    71  
    72  // Delete attempts to remove any mapping from the key
    73  func (db *DatabaseClient) Delete(key []byte) error {
    74  	resp, err := db.client.Delete(context.Background(), &rpcdbpb.DeleteRequest{
    75  		Key: key,
    76  	})
    77  	if err != nil {
    78  		return err
    79  	}
    80  	return ErrEnumToError[resp.Err]
    81  }
    82  
    83  // NewBatch returns a new batch
    84  func (db *DatabaseClient) NewBatch() database.Batch {
    85  	return &batch{db: db}
    86  }
    87  
    88  func (db *DatabaseClient) NewIterator() database.Iterator {
    89  	return db.NewIteratorWithStartAndPrefix(nil, nil)
    90  }
    91  
    92  func (db *DatabaseClient) NewIteratorWithStart(start []byte) database.Iterator {
    93  	return db.NewIteratorWithStartAndPrefix(start, nil)
    94  }
    95  
    96  func (db *DatabaseClient) NewIteratorWithPrefix(prefix []byte) database.Iterator {
    97  	return db.NewIteratorWithStartAndPrefix(nil, prefix)
    98  }
    99  
   100  // NewIteratorWithStartAndPrefix returns a new empty iterator
   101  func (db *DatabaseClient) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
   102  	resp, err := db.client.NewIteratorWithStartAndPrefix(context.Background(), &rpcdbpb.NewIteratorWithStartAndPrefixRequest{
   103  		Start:  start,
   104  		Prefix: prefix,
   105  	})
   106  	if err != nil {
   107  		return &database.IteratorError{
   108  			Err: err,
   109  		}
   110  	}
   111  	return newIterator(db, resp.Id)
   112  }
   113  
   114  // Compact attempts to optimize the space utilization in the provided range
   115  func (db *DatabaseClient) Compact(start, limit []byte) error {
   116  	resp, err := db.client.Compact(context.Background(), &rpcdbpb.CompactRequest{
   117  		Start: start,
   118  		Limit: limit,
   119  	})
   120  	if err != nil {
   121  		return err
   122  	}
   123  	return ErrEnumToError[resp.Err]
   124  }
   125  
   126  // Close attempts to close the database
   127  func (db *DatabaseClient) Close() error {
   128  	db.closed.Set(true)
   129  	resp, err := db.client.Close(context.Background(), &rpcdbpb.CloseRequest{})
   130  	if err != nil {
   131  		return err
   132  	}
   133  	return ErrEnumToError[resp.Err]
   134  }
   135  
   136  func (db *DatabaseClient) HealthCheck(ctx context.Context) (interface{}, error) {
   137  	health, err := db.client.HealthCheck(ctx, &emptypb.Empty{})
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return json.RawMessage(health.Details), nil
   143  }
   144  
   145  type batch struct {
   146  	database.BatchOps
   147  
   148  	db *DatabaseClient
   149  }
   150  
   151  func (b *batch) Write() error {
   152  	request := &rpcdbpb.WriteBatchRequest{}
   153  	keySet := set.NewSet[string](len(b.Ops))
   154  	for i := len(b.Ops) - 1; i >= 0; i-- {
   155  		op := b.Ops[i]
   156  		key := string(op.Key)
   157  		if keySet.Contains(key) {
   158  			continue
   159  		}
   160  		keySet.Add(key)
   161  
   162  		if op.Delete {
   163  			request.Deletes = append(request.Deletes, &rpcdbpb.DeleteRequest{
   164  				Key: op.Key,
   165  			})
   166  		} else {
   167  			request.Puts = append(request.Puts, &rpcdbpb.PutRequest{
   168  				Key:   op.Key,
   169  				Value: op.Value,
   170  			})
   171  		}
   172  	}
   173  
   174  	resp, err := b.db.client.WriteBatch(context.Background(), request)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	return ErrEnumToError[resp.Err]
   179  }
   180  
   181  func (b *batch) Inner() database.Batch {
   182  	return b
   183  }
   184  
   185  type iterator struct {
   186  	db *DatabaseClient
   187  	id uint64
   188  
   189  	data        []*rpcdbpb.PutRequest
   190  	fetchedData chan []*rpcdbpb.PutRequest
   191  
   192  	errLock sync.RWMutex
   193  	err     error
   194  
   195  	reqUpdateError chan chan struct{}
   196  
   197  	once     sync.Once
   198  	onClose  chan struct{}
   199  	onClosed chan struct{}
   200  }
   201  
   202  func newIterator(db *DatabaseClient, id uint64) *iterator {
   203  	it := &iterator{
   204  		db:             db,
   205  		id:             id,
   206  		fetchedData:    make(chan []*rpcdbpb.PutRequest),
   207  		reqUpdateError: make(chan chan struct{}),
   208  		onClose:        make(chan struct{}),
   209  		onClosed:       make(chan struct{}),
   210  	}
   211  	go it.fetch()
   212  	return it
   213  }
   214  
   215  // Invariant: fetch is the only thread with access to send requests to the
   216  // server's iterator. This is needed because iterators are not thread safe and
   217  // the server expects the client (us) to only ever issue one request at a time
   218  // for a given iterator id.
   219  func (it *iterator) fetch() {
   220  	defer func() {
   221  		resp, err := it.db.client.IteratorRelease(context.Background(), &rpcdbpb.IteratorReleaseRequest{
   222  			Id: it.id,
   223  		})
   224  		if err != nil {
   225  			it.setError(err)
   226  		} else {
   227  			it.setError(ErrEnumToError[resp.Err])
   228  		}
   229  
   230  		close(it.fetchedData)
   231  		close(it.onClosed)
   232  	}()
   233  
   234  	for {
   235  		resp, err := it.db.client.IteratorNext(context.Background(), &rpcdbpb.IteratorNextRequest{
   236  			Id: it.id,
   237  		})
   238  		if err != nil {
   239  			it.setError(err)
   240  			return
   241  		}
   242  
   243  		if len(resp.Data) == 0 {
   244  			return
   245  		}
   246  
   247  		for {
   248  			select {
   249  			case it.fetchedData <- resp.Data:
   250  			case onUpdated := <-it.reqUpdateError:
   251  				it.updateError()
   252  				close(onUpdated)
   253  				continue
   254  			case <-it.onClose:
   255  				return
   256  			}
   257  			break
   258  		}
   259  	}
   260  }
   261  
   262  // Next attempts to move the iterator to the next element and returns if this
   263  // succeeded
   264  func (it *iterator) Next() bool {
   265  	if it.db.closed.Get() {
   266  		it.data = nil
   267  		it.setError(database.ErrClosed)
   268  		return false
   269  	}
   270  	if len(it.data) > 1 {
   271  		it.data[0] = nil
   272  		it.data = it.data[1:]
   273  		return true
   274  	}
   275  
   276  	it.data = <-it.fetchedData
   277  	return len(it.data) > 0
   278  }
   279  
   280  // Error returns any that occurred while iterating
   281  func (it *iterator) Error() error {
   282  	if err := it.getError(); err != nil {
   283  		return err
   284  	}
   285  
   286  	onUpdated := make(chan struct{})
   287  	select {
   288  	case it.reqUpdateError <- onUpdated:
   289  		<-onUpdated
   290  	case <-it.onClosed:
   291  	}
   292  
   293  	return it.getError()
   294  }
   295  
   296  // Key returns the key of the current element
   297  func (it *iterator) Key() []byte {
   298  	if len(it.data) == 0 {
   299  		return nil
   300  	}
   301  	return it.data[0].Key
   302  }
   303  
   304  // Value returns the value of the current element
   305  func (it *iterator) Value() []byte {
   306  	if len(it.data) == 0 {
   307  		return nil
   308  	}
   309  	return it.data[0].Value
   310  }
   311  
   312  // Release frees any resources held by the iterator
   313  func (it *iterator) Release() {
   314  	it.once.Do(func() {
   315  		close(it.onClose)
   316  		<-it.onClosed
   317  	})
   318  }
   319  
   320  func (it *iterator) updateError() {
   321  	resp, err := it.db.client.IteratorError(context.Background(), &rpcdbpb.IteratorErrorRequest{
   322  		Id: it.id,
   323  	})
   324  	if err != nil {
   325  		it.setError(err)
   326  	} else {
   327  		it.setError(ErrEnumToError[resp.Err])
   328  	}
   329  }
   330  
   331  func (it *iterator) setError(err error) {
   332  	if err == nil {
   333  		return
   334  	}
   335  
   336  	it.errLock.Lock()
   337  	defer it.errLock.Unlock()
   338  
   339  	if it.err == nil {
   340  		it.err = err
   341  	}
   342  }
   343  
   344  func (it *iterator) getError() error {
   345  	it.errLock.RLock()
   346  	defer it.errLock.RUnlock()
   347  
   348  	return it.err
   349  }