github.com/tendermint/tmlibs@v0.9.0/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  	"github.com/tendermint/tmlibs/db"
    13  	protodb "github.com/tendermint/tmlibs/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  //  * leveldb
    61  // See https://godoc.org/github.com/tendermint/tmlibs/db#DBBackendType
    62  func (s *server) Init(ctx context.Context, in *protodb.Init) (*protodb.Entity, error) {
    63  	s.mu.Lock()
    64  	defer s.mu.Unlock()
    65  
    66  	s.db = db.NewDB(in.Name, db.DBBackendType(in.Type), in.Dir)
    67  	return &protodb.Entity{CreatedAt: time.Now().Unix()}, nil
    68  }
    69  
    70  func (s *server) Delete(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
    71  	s.db.Delete(in.Key)
    72  	return nothing, nil
    73  }
    74  
    75  var nothing = new(protodb.Nothing)
    76  
    77  func (s *server) DeleteSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
    78  	s.db.DeleteSync(in.Key)
    79  	return nothing, nil
    80  }
    81  
    82  func (s *server) Get(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) {
    83  	value := s.db.Get(in.Key)
    84  	return &protodb.Entity{Value: value}, nil
    85  }
    86  
    87  func (s *server) GetStream(ds protodb.DB_GetStreamServer) error {
    88  	// Receive routine
    89  	responsesChan := make(chan *protodb.Entity)
    90  	go func() {
    91  		defer close(responsesChan)
    92  		ctx := context.Background()
    93  		for {
    94  			in, err := ds.Recv()
    95  			if err != nil {
    96  				responsesChan <- &protodb.Entity{Err: err.Error()}
    97  				return
    98  			}
    99  			out, err := s.Get(ctx, in)
   100  			if err != nil {
   101  				if out == nil {
   102  					out = new(protodb.Entity)
   103  					out.Key = in.Key
   104  				}
   105  				out.Err = err.Error()
   106  				responsesChan <- out
   107  				return
   108  			}
   109  
   110  			// Otherwise continue on
   111  			responsesChan <- out
   112  		}
   113  	}()
   114  
   115  	// Send routine, block until we return
   116  	for out := range responsesChan {
   117  		if err := ds.Send(out); err != nil {
   118  			return err
   119  		}
   120  	}
   121  	return nil
   122  }
   123  
   124  func (s *server) Has(ctx context.Context, in *protodb.Entity) (*protodb.Entity, error) {
   125  	exists := s.db.Has(in.Key)
   126  	return &protodb.Entity{Exists: exists}, nil
   127  }
   128  
   129  func (s *server) Set(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
   130  	s.db.Set(in.Key, in.Value)
   131  	return nothing, nil
   132  }
   133  
   134  func (s *server) SetSync(ctx context.Context, in *protodb.Entity) (*protodb.Nothing, error) {
   135  	s.db.SetSync(in.Key, in.Value)
   136  	return nothing, nil
   137  }
   138  
   139  func (s *server) Iterator(query *protodb.Entity, dis protodb.DB_IteratorServer) error {
   140  	it := s.db.Iterator(query.Start, query.End)
   141  	return s.handleIterator(it, dis.Send)
   142  }
   143  
   144  func (s *server) handleIterator(it db.Iterator, sendFunc func(*protodb.Iterator) error) error {
   145  	for it.Valid() {
   146  		start, end := it.Domain()
   147  		out := &protodb.Iterator{
   148  			Domain: &protodb.Domain{Start: start, End: end},
   149  			Valid:  it.Valid(),
   150  			Key:    it.Key(),
   151  			Value:  it.Value(),
   152  		}
   153  		if err := sendFunc(out); err != nil {
   154  			return err
   155  		}
   156  
   157  		// Finally move the iterator forward
   158  		it.Next()
   159  	}
   160  	return nil
   161  }
   162  
   163  func (s *server) ReverseIterator(query *protodb.Entity, dis protodb.DB_ReverseIteratorServer) error {
   164  	it := s.db.ReverseIterator(query.Start, query.End)
   165  	return s.handleIterator(it, dis.Send)
   166  }
   167  
   168  func (s *server) Stats(context.Context, *protodb.Nothing) (*protodb.Stats, error) {
   169  	stats := s.db.Stats()
   170  	return &protodb.Stats{Data: stats, TimeAt: time.Now().Unix()}, nil
   171  }
   172  
   173  func (s *server) BatchWrite(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) {
   174  	return s.batchWrite(c, b, false)
   175  }
   176  
   177  func (s *server) BatchWriteSync(c context.Context, b *protodb.Batch) (*protodb.Nothing, error) {
   178  	return s.batchWrite(c, b, true)
   179  }
   180  
   181  func (s *server) batchWrite(c context.Context, b *protodb.Batch, sync bool) (*protodb.Nothing, error) {
   182  	bat := s.db.NewBatch()
   183  	for _, op := range b.Ops {
   184  		switch op.Type {
   185  		case protodb.Operation_SET:
   186  			bat.Set(op.Entity.Key, op.Entity.Value)
   187  		case protodb.Operation_DELETE:
   188  			bat.Delete(op.Entity.Key)
   189  		}
   190  	}
   191  	if sync {
   192  		bat.WriteSync()
   193  	} else {
   194  		bat.Write()
   195  	}
   196  	return nothing, nil
   197  }