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 }