github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/tm-db/remotedb/grpcdb/server.go (about)

     1  package grpcdb
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"sync"
     7  	"time"
     8  
     9  	"google.golang.org/grpc"
    10  	"google.golang.org/grpc/credentials"
    11  
    12  	db "github.com/fibonacci-chain/fbc/libs/tm-db"
    13  	protodb "github.com/fibonacci-chain/fbc/libs/tm-db/remotedb/proto"
    14  )
    15  
    16  // ListenAndServe is a blocking function that sets up a gRPC based
    17  // server at the address supplied, with the gRPC options passed in.
    18  // Normally in usage, invoke it in a goroutine like you would for http.ListenAndServe.
    19  func ListenAndServe(addr, cert, key string, opts ...grpc.ServerOption) error {
    20  	ln, err := net.Listen("tcp", addr)
    21  	if err != nil {
    22  		return err
    23  	}
    24  	srv, err := NewServer(cert, key, opts...)
    25  	if err != nil {
    26  		return err
    27  	}
    28  	return srv.Serve(ln)
    29  }
    30  
    31  func NewServer(cert, key string, opts ...grpc.ServerOption) (*grpc.Server, error) {
    32  	creds, err := credentials.NewServerTLSFromFile(cert, key)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	opts = append(opts, grpc.Creds(creds))
    37  	srv := grpc.NewServer(opts...)
    38  	protodb.RegisterDBServer(srv, new(server))
    39  	return srv, nil
    40  }
    41  
    42  type server struct {
    43  	mu sync.Mutex
    44  	db db.DB
    45  }
    46  
    47  var _ protodb.DBServer = (*server)(nil)
    48  
    49  // Init initializes the server's database. Only one type of database
    50  // can be initialized per server.
    51  //
    52  // Dir is the directory on the file system in which the DB will be stored(if backed by disk) (TODO: remove)
    53  //
    54  // # Name is representative filesystem entry's basepath
    55  //
    56  // Type can be either one of:
    57  //   - cleveldb (if built with gcc enabled)
    58  //   - fsdb
    59  //   - memdB
    60  //   - goleveldb
    61  //
    62  // See https://godoc.org/github.com/tendermint/tendermint/libs/db#BackendType
    63  func (s *server) Init(ctx context.Context, in *protodb.Init) (*protodb.Entity, error) {
    64  	s.mu.Lock()
    65  	defer s.mu.Unlock()
    66  
    67  	s.db = db.NewDB(in.Name, db.BackendType(in.Type), in.Dir)
    68  	return &protodb.Entity{CreatedAt: time.Now().Unix()}, nil
    69  }
    70  
    71  func (s *server) Delete(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
    72  	err := s.db.Delete(in.Key)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return nothing, nil
    77  }
    78  
    79  var nothing = new(protodb.Nothing)
    80  
    81  func (s *server) DeleteSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
    82  	err := s.db.DeleteSync(in.Key)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return nothing, nil
    87  }
    88  
    89  func (s *server) Get(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) {
    90  	value, err := s.db.Get(in.Key)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	return &protodb.Entity{Value: value}, nil
    95  }
    96  
    97  func (s *server) GetStream(ds protodb.DB_GetStreamServer) error {
    98  	// Receive routine
    99  	responsesChan := make(chan *protodb.Entity)
   100  	go func() {
   101  		defer close(responsesChan)
   102  		ctx := context.Background()
   103  		for {
   104  			in, err := ds.Recv()
   105  			if err != nil {
   106  				responsesChan <- &protodb.Entity{Err: err.Error()}
   107  				return
   108  			}
   109  			out, err := s.Get(ctx, in)
   110  			if err != nil {
   111  				if out == nil {
   112  					out = new(protodb.Entity)
   113  					out.Key = in.Key
   114  				}
   115  				out.Err = err.Error()
   116  				responsesChan <- out
   117  				return
   118  			}
   119  
   120  			// Otherwise continue on
   121  			responsesChan <- out
   122  		}
   123  	}()
   124  
   125  	// Send routine, block until we return
   126  	for out := range responsesChan {
   127  		if err := ds.Send(out); err != nil {
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }
   133  
   134  func (s *server) Has(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) {
   135  	exists, err := s.db.Has(in.Key)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	return &protodb.Entity{Exists: exists}, nil
   140  }
   141  
   142  func (s *server) Set(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
   143  	err := s.db.Set(in.Key, in.Value)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	return nothing, nil
   148  }
   149  
   150  func (s *server) SetSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
   151  	err := s.db.SetSync(in.Key, in.Value)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return nothing, nil
   156  }
   157  
   158  func (s *server) Iterator(query *protodb.Entity, dis protodb.DB_IteratorServer) error {
   159  	it, err := s.db.Iterator(query.Start, query.End)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	defer it.Close()
   164  	return s.handleIterator(it, dis.Send)
   165  }
   166  
   167  func (s *server) handleIterator(it db.Iterator, sendFunc func(*protodb.Iterator) error) error {
   168  	for it.Valid() {
   169  		start, end := it.Domain()
   170  		key := it.Key()
   171  		value := it.Value()
   172  
   173  		out := &protodb.Iterator{
   174  			Domain: &protodb.Domain{Start: start, End: end},
   175  			Valid:  it.Valid(),
   176  			Key:    key,
   177  			Value:  value,
   178  		}
   179  		if err := sendFunc(out); err != nil {
   180  			return err
   181  		}
   182  
   183  		// Finally move the iterator forward,
   184  		it.Next()
   185  
   186  	}
   187  	return nil
   188  }
   189  
   190  func (s *server) ReverseIterator(query *protodb.Entity, dis protodb.DB_ReverseIteratorServer) error {
   191  	it, err := s.db.ReverseIterator(query.Start, query.End)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	defer it.Close()
   196  	return s.handleIterator(it, dis.Send)
   197  }
   198  
   199  func (s *server) Stats(context.Context, *protodb.Nothing) (*protodb.Stats, error) {
   200  	stats := s.db.Stats()
   201  	return &protodb.Stats{Data: stats, TimeAt: time.Now().Unix()}, nil
   202  }
   203  
   204  func (s *server) BatchWrite(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) {
   205  	return s.batchWrite(c, b, false)
   206  }
   207  
   208  func (s *server) BatchWriteSync(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) {
   209  	return s.batchWrite(c, b, true)
   210  }
   211  
   212  func (s *server) batchWrite(c context.Context, b *protodb.Batch, sync bool) (*protodb.Nothing, error) {
   213  	bat := s.db.NewBatch()
   214  	defer bat.Close()
   215  	for _, op := range b.Ops {
   216  		switch op.Type {
   217  		case protodb.Operation_SET:
   218  			bat.Set(op.Entity.Key, op.Entity.Value)
   219  		case protodb.Operation_DELETE:
   220  			bat.Delete(op.Entity.Key)
   221  		}
   222  	}
   223  	if sync {
   224  		err := bat.WriteSync()
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  	} else {
   229  		err := bat.Write()
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  	}
   234  	return nothing, nil
   235  }