github.com/annwntech/go-micro/v2@v2.9.5/store/service/service.go (about)

     1  // Package service implements the store service interface
     2  package service
     3  
     4  import (
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"time"
    10  
    11  	"github.com/annwntech/go-micro/v2/client"
    12  	"github.com/annwntech/go-micro/v2/errors"
    13  	"github.com/annwntech/go-micro/v2/metadata"
    14  	"github.com/annwntech/go-micro/v2/store"
    15  	pb "github.com/annwntech/go-micro/v2/store/service/proto"
    16  )
    17  
    18  type serviceStore struct {
    19  	options store.Options
    20  
    21  	// The database to use
    22  	Database string
    23  
    24  	// The table to use
    25  	Table string
    26  
    27  	// Addresses of the nodes
    28  	Nodes []string
    29  
    30  	// store service client
    31  	Client pb.StoreService
    32  }
    33  
    34  func (s *serviceStore) Close() error {
    35  	return nil
    36  }
    37  
    38  func (s *serviceStore) Init(opts ...store.Option) error {
    39  	for _, o := range opts {
    40  		o(&s.options)
    41  	}
    42  	s.Database = s.options.Database
    43  	s.Table = s.options.Table
    44  	s.Nodes = s.options.Nodes
    45  
    46  	return nil
    47  }
    48  
    49  func (s *serviceStore) Context() context.Context {
    50  	ctx := context.Background()
    51  	md := make(metadata.Metadata)
    52  	if len(s.Database) > 0 {
    53  		md["Micro-Database"] = s.Database
    54  	}
    55  
    56  	if len(s.Table) > 0 {
    57  		md["Micro-Table"] = s.Table
    58  	}
    59  	return metadata.NewContext(ctx, md)
    60  }
    61  
    62  // Sync all the known records
    63  func (s *serviceStore) List(opts ...store.ListOption) ([]string, error) {
    64  	options := store.ListOptions{
    65  		Database: s.Database,
    66  		Table:    s.Table,
    67  	}
    68  
    69  	for _, o := range opts {
    70  		o(&options)
    71  	}
    72  
    73  	listOpts := &pb.ListOptions{
    74  		Database: options.Database,
    75  		Table:    options.Table,
    76  		Prefix:   options.Prefix,
    77  		Suffix:   options.Suffix,
    78  		Limit:    uint64(options.Limit),
    79  		Offset:   uint64(options.Offset),
    80  	}
    81  
    82  	stream, err := s.Client.List(s.Context(), &pb.ListRequest{Options: listOpts}, client.WithAddress(s.Nodes...))
    83  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
    84  		return nil, store.ErrNotFound
    85  	} else if err != nil {
    86  		return nil, err
    87  	}
    88  	defer stream.Close()
    89  
    90  	var keys []string
    91  
    92  	for {
    93  		rsp, err := stream.Recv()
    94  		if err == io.EOF {
    95  			break
    96  		}
    97  		if err != nil {
    98  			return keys, err
    99  		}
   100  
   101  		for _, key := range rsp.Keys {
   102  			keys = append(keys, key)
   103  		}
   104  	}
   105  
   106  	return keys, nil
   107  }
   108  
   109  // Read a record with key
   110  func (s *serviceStore) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) {
   111  	options := store.ReadOptions{
   112  		Database: s.Database,
   113  		Table:    s.Table,
   114  	}
   115  
   116  	for _, o := range opts {
   117  		o(&options)
   118  	}
   119  
   120  	readOpts := &pb.ReadOptions{
   121  		Database: options.Database,
   122  		Table:    options.Table,
   123  		Prefix:   options.Prefix,
   124  		Suffix:   options.Suffix,
   125  		Limit:    uint64(options.Limit),
   126  		Offset:   uint64(options.Offset),
   127  	}
   128  
   129  	rsp, err := s.Client.Read(s.Context(), &pb.ReadRequest{
   130  		Key:     key,
   131  		Options: readOpts,
   132  	}, client.WithAddress(s.Nodes...))
   133  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   134  		return nil, store.ErrNotFound
   135  	} else if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	records := make([]*store.Record, 0, len(rsp.Records))
   140  
   141  	for _, val := range rsp.Records {
   142  		metadata := make(map[string]interface{})
   143  
   144  		for k, v := range val.Metadata {
   145  			switch v.Type {
   146  			// TODO: parse all types
   147  			default:
   148  				metadata[k] = v
   149  			}
   150  		}
   151  
   152  		records = append(records, &store.Record{
   153  			Key:      val.Key,
   154  			Value:    val.Value,
   155  			Expiry:   time.Duration(val.Expiry) * time.Second,
   156  			Metadata: metadata,
   157  		})
   158  	}
   159  
   160  	return records, nil
   161  }
   162  
   163  // Write a record
   164  func (s *serviceStore) Write(record *store.Record, opts ...store.WriteOption) error {
   165  	options := store.WriteOptions{
   166  		Database: s.Database,
   167  		Table:    s.Table,
   168  	}
   169  
   170  	for _, o := range opts {
   171  		o(&options)
   172  	}
   173  
   174  	writeOpts := &pb.WriteOptions{
   175  		Database: options.Database,
   176  		Table:    options.Table,
   177  	}
   178  
   179  	metadata := make(map[string]*pb.Field)
   180  
   181  	for k, v := range record.Metadata {
   182  		metadata[k] = &pb.Field{
   183  			Type:  reflect.TypeOf(v).String(),
   184  			Value: fmt.Sprintf("%v", v),
   185  		}
   186  	}
   187  
   188  	_, err := s.Client.Write(s.Context(), &pb.WriteRequest{
   189  		Record: &pb.Record{
   190  			Key:      record.Key,
   191  			Value:    record.Value,
   192  			Expiry:   int64(record.Expiry.Seconds()),
   193  			Metadata: metadata,
   194  		},
   195  		Options: writeOpts}, client.WithAddress(s.Nodes...))
   196  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   197  		return store.ErrNotFound
   198  	}
   199  
   200  	return err
   201  }
   202  
   203  // Delete a record with key
   204  func (s *serviceStore) Delete(key string, opts ...store.DeleteOption) error {
   205  	options := store.DeleteOptions{
   206  		Database: s.Database,
   207  		Table:    s.Table,
   208  	}
   209  
   210  	for _, o := range opts {
   211  		o(&options)
   212  	}
   213  
   214  	deleteOpts := &pb.DeleteOptions{
   215  		Database: options.Database,
   216  		Table:    options.Table,
   217  	}
   218  
   219  	_, err := s.Client.Delete(s.Context(), &pb.DeleteRequest{
   220  		Key:     key,
   221  		Options: deleteOpts,
   222  	}, client.WithAddress(s.Nodes...))
   223  	if err != nil && errors.Equal(err, errors.NotFound("", "")) {
   224  		return store.ErrNotFound
   225  	}
   226  
   227  	return err
   228  }
   229  
   230  func (s *serviceStore) String() string {
   231  	return "service"
   232  }
   233  
   234  func (s *serviceStore) Options() store.Options {
   235  	return s.options
   236  }
   237  
   238  // NewStore returns a new store service implementation
   239  func NewStore(opts ...store.Option) store.Store {
   240  	var options store.Options
   241  	for _, o := range opts {
   242  		o(&options)
   243  	}
   244  
   245  	if options.Client == nil {
   246  		options.Client = client.DefaultClient
   247  	}
   248  
   249  	service := &serviceStore{
   250  		options:  options,
   251  		Database: options.Database,
   252  		Table:    options.Table,
   253  		Nodes:    options.Nodes,
   254  		Client:   pb.NewStoreService("go.micro.store", options.Client),
   255  	}
   256  
   257  	return service
   258  }