github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/proxy/store.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package proxy 19 20 import ( 21 "bytes" 22 "github.com/aacfactory/errors" 23 "github.com/aacfactory/fns/commons/signatures" 24 "github.com/aacfactory/fns/context" 25 "github.com/aacfactory/fns/shareds" 26 "github.com/aacfactory/fns/transports" 27 "github.com/aacfactory/json" 28 "strconv" 29 "time" 30 ) 31 32 var ( 33 sharedHeaderStoreValue = []byte("store") 34 ) 35 36 type Store struct { 37 client ClientFetcher 38 signature signatures.Signature 39 } 40 41 type StoreGetParam struct { 42 Key []byte `json:"key"` 43 } 44 45 type StoreGetResult struct { 46 Value []byte `json:"value"` 47 Has bool `json:"has"` 48 Error json.RawMessage `json:"error"` 49 } 50 51 func (store *Store) Get(ctx context.Context, key []byte) (value []byte, has bool, err error) { 52 // param 53 param := StoreGetParam{ 54 Key: key, 55 } 56 p, _ := json.Marshal(param) 57 command := Command{ 58 Command: "get", 59 Payload: p, 60 } 61 body, _ := json.Marshal(command) 62 63 header := transports.AcquireHeader() 64 defer transports.ReleaseHeader(header) 65 header.Set(transports.ContentTypeHeaderName, contentType) 66 header.Set(sharedHeader, sharedHeaderStoreValue) 67 // signature 68 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 69 // do 70 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 71 if doErr != nil { 72 err = errors.Warning("fns: development store get failed").WithCause(doErr) 73 return 74 } 75 if status == 200 { 76 result := StoreGetResult{} 77 decodeErr := json.Unmarshal(responseBody, &result) 78 if decodeErr != nil { 79 err = errors.Warning("fns: development store get failed").WithCause(decodeErr) 80 return 81 } 82 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 83 err = errors.Decode(result.Error) 84 return 85 } 86 value = result.Value 87 has = result.Has 88 return 89 } 90 err = errors.Warning("fns: development store get failed").WithMeta("status", strconv.Itoa(status)) 91 return 92 } 93 94 type StoreSetParam struct { 95 Key []byte `json:"key"` 96 Value []byte `json:"value"` 97 } 98 99 type StoreSetResult struct { 100 Error json.RawMessage `json:"error"` 101 } 102 103 func (store *Store) Set(ctx context.Context, key []byte, value []byte) (err error) { 104 // param 105 param := StoreSetParam{ 106 Key: key, 107 Value: value, 108 } 109 p, _ := json.Marshal(param) 110 command := Command{ 111 Command: "set", 112 Payload: p, 113 } 114 body, _ := json.Marshal(command) 115 116 header := transports.AcquireHeader() 117 defer transports.ReleaseHeader(header) 118 header.Set(transports.ContentTypeHeaderName, contentType) 119 header.Set(sharedHeader, sharedHeaderStoreValue) 120 // signature 121 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 122 // do 123 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 124 if doErr != nil { 125 err = errors.Warning("fns: development store set failed").WithCause(doErr) 126 return 127 } 128 if status == 200 { 129 result := StoreSetResult{} 130 decodeErr := json.Unmarshal(responseBody, &result) 131 if decodeErr != nil { 132 err = errors.Warning("fns: development store set failed").WithCause(decodeErr) 133 return 134 } 135 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 136 err = errors.Decode(result.Error) 137 return 138 } 139 return 140 } 141 err = errors.Warning("fns: development store set failed").WithMeta("status", strconv.Itoa(status)) 142 return 143 } 144 145 type StoreSetWithTTLParam struct { 146 Key []byte `json:"key"` 147 Value []byte `json:"value"` 148 TTL time.Duration `json:"ttl"` 149 } 150 151 type StoreSetWithTTLResult struct { 152 Error json.RawMessage `json:"error"` 153 } 154 155 func (store *Store) SetWithTTL(ctx context.Context, key []byte, value []byte, ttl time.Duration) (err error) { 156 // param 157 param := StoreSetWithTTLParam{ 158 Key: key, 159 Value: value, 160 TTL: ttl, 161 } 162 p, _ := json.Marshal(param) 163 command := Command{ 164 Command: "setWithTTL", 165 Payload: p, 166 } 167 body, _ := json.Marshal(command) 168 169 header := transports.AcquireHeader() 170 defer transports.ReleaseHeader(header) 171 header.Set(transports.ContentTypeHeaderName, contentType) 172 header.Set(sharedHeader, sharedHeaderStoreValue) 173 // signature 174 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 175 // do 176 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 177 if doErr != nil { 178 err = errors.Warning("fns: development store set with ttl failed").WithCause(doErr) 179 return 180 } 181 if status == 200 { 182 result := StoreSetWithTTLResult{} 183 decodeErr := json.Unmarshal(responseBody, &result) 184 if decodeErr != nil { 185 err = errors.Warning("fns: development store set with ttl failed").WithCause(decodeErr) 186 return 187 } 188 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 189 err = errors.Decode(result.Error) 190 return 191 } 192 return 193 } 194 err = errors.Warning("fns: development store set with ttl failed").WithMeta("status", strconv.Itoa(status)) 195 return 196 } 197 198 type StoreIncrParam struct { 199 Key []byte `json:"key"` 200 Delta int64 `json:"delta"` 201 } 202 203 type StoreIncrResult struct { 204 N int64 `json:"n"` 205 Error json.RawMessage `json:"error"` 206 } 207 208 func (store *Store) Incr(ctx context.Context, key []byte, delta int64) (v int64, err error) { 209 // param 210 param := StoreIncrParam{ 211 Key: key, 212 Delta: delta, 213 } 214 p, _ := json.Marshal(param) 215 command := Command{ 216 Command: "incr", 217 Payload: p, 218 } 219 body, _ := json.Marshal(command) 220 221 header := transports.AcquireHeader() 222 defer transports.ReleaseHeader(header) 223 header.Set(transports.ContentTypeHeaderName, contentType) 224 header.Set(sharedHeader, sharedHeaderStoreValue) 225 // signature 226 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 227 // do 228 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 229 if doErr != nil { 230 err = errors.Warning("fns: development store incr failed").WithCause(doErr) 231 return 232 } 233 if status == 200 { 234 result := StoreIncrResult{} 235 decodeErr := json.Unmarshal(responseBody, &result) 236 if decodeErr != nil { 237 err = errors.Warning("fns: development store incr failed").WithCause(decodeErr) 238 return 239 } 240 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 241 err = errors.Decode(result.Error) 242 return 243 } 244 v = result.N 245 return 246 } 247 err = errors.Warning("fns: development store incr failed").WithMeta("status", strconv.Itoa(status)) 248 return 249 } 250 251 type StoreRemoveParam struct { 252 Key []byte `json:"key"` 253 } 254 255 type StoreRemoveResult struct { 256 Error json.RawMessage `json:"error"` 257 } 258 259 func (store *Store) Remove(ctx context.Context, key []byte) (err error) { 260 // param 261 param := StoreRemoveParam{ 262 Key: key, 263 } 264 p, _ := json.Marshal(param) 265 command := Command{ 266 Command: "remove", 267 Payload: p, 268 } 269 body, _ := json.Marshal(command) 270 271 header := transports.AcquireHeader() 272 defer transports.ReleaseHeader(header) 273 header.Set(transports.ContentTypeHeaderName, contentType) 274 header.Set(sharedHeader, sharedHeaderStoreValue) 275 // signature 276 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 277 // do 278 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 279 if doErr != nil { 280 err = errors.Warning("fns: development store remove failed").WithCause(doErr) 281 return 282 } 283 if status == 200 { 284 result := StoreRemoveResult{} 285 decodeErr := json.Unmarshal(responseBody, &result) 286 if decodeErr != nil { 287 err = errors.Warning("fns: development store remove failed").WithCause(decodeErr) 288 return 289 } 290 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 291 err = errors.Decode(result.Error) 292 return 293 } 294 return 295 } 296 err = errors.Warning("fns: development store remove failed").WithMeta("status", strconv.Itoa(status)) 297 return 298 } 299 300 type StoreExpireParam struct { 301 Key []byte `json:"key"` 302 TTL time.Duration `json:"ttl"` 303 } 304 305 type StoreExpireResult struct { 306 Error json.RawMessage `json:"error"` 307 } 308 309 func (store *Store) Expire(ctx context.Context, key []byte, ttl time.Duration) (err error) { 310 // param 311 param := StoreExpireParam{ 312 Key: key, 313 TTL: ttl, 314 } 315 p, _ := json.Marshal(param) 316 command := Command{ 317 Command: "expire", 318 Payload: p, 319 } 320 body, _ := json.Marshal(command) 321 322 header := transports.AcquireHeader() 323 defer transports.ReleaseHeader(header) 324 header.Set(transports.ContentTypeHeaderName, contentType) 325 header.Set(sharedHeader, sharedHeaderStoreValue) 326 // signature 327 header.Set(transports.SignatureHeaderName, store.signature.Sign(body)) 328 // do 329 status, _, responseBody, doErr := store.client().Do(ctx, transports.MethodPost, sharedHandlerPath, header, body) 330 if doErr != nil { 331 err = errors.Warning("fns: development store expire failed").WithCause(doErr) 332 return 333 } 334 if status == 200 { 335 result := StoreExpireResult{} 336 decodeErr := json.Unmarshal(responseBody, &result) 337 if decodeErr != nil { 338 err = errors.Warning("fns: development store expire failed").WithCause(decodeErr) 339 return 340 } 341 if len(result.Error) > 0 && !bytes.Equal(result.Error, json.NullBytes) { 342 err = errors.Decode(result.Error) 343 return 344 } 345 return 346 } 347 err = errors.Warning("fns: development store expire failed").WithMeta("status", strconv.Itoa(status)) 348 return 349 } 350 351 func (store *Store) Close() {} 352 353 // +-------------------------------------------------------------------------------------------------------------------+ 354 355 func NewSharedStoreHandler(store shareds.Store) transports.Handler { 356 return &SharedStoreHandler{ 357 store: store, 358 } 359 } 360 361 type SharedStoreHandler struct { 362 store shareds.Store 363 } 364 365 func (handler *SharedStoreHandler) Handle(w transports.ResponseWriter, r transports.Request) { 366 body, bodyErr := r.Body() 367 if bodyErr != nil { 368 w.Failed(ErrInvalidBody) 369 return 370 } 371 cmd := Command{} 372 decodeErr := json.Unmarshal(body, &cmd) 373 if decodeErr != nil { 374 w.Failed(ErrInvalidBody.WithCause(decodeErr)) 375 return 376 } 377 switch cmd.Command { 378 case "get": 379 param := StoreGetParam{} 380 paramErr := json.Unmarshal(cmd.Payload, ¶m) 381 if paramErr != nil { 382 w.Failed(ErrInvalidBody.WithCause(paramErr)) 383 return 384 } 385 result := StoreGetResult{} 386 value, has, err := handler.store.Get(r, param.Key) 387 if err == nil { 388 result.Value = value 389 result.Has = has 390 } else { 391 result.Error, _ = json.Marshal(errors.Wrap(err)) 392 } 393 w.Succeed(result) 394 break 395 case "set": 396 param := StoreSetParam{} 397 paramErr := json.Unmarshal(cmd.Payload, ¶m) 398 if paramErr != nil { 399 w.Failed(ErrInvalidBody.WithCause(paramErr)) 400 return 401 } 402 result := StoreSetResult{} 403 err := handler.store.Set(r, param.Key, param.Value) 404 if err == nil { 405 } else { 406 result.Error, _ = json.Marshal(errors.Wrap(err)) 407 } 408 w.Succeed(result) 409 break 410 case "setWithTTL": 411 param := StoreSetWithTTLParam{} 412 paramErr := json.Unmarshal(cmd.Payload, ¶m) 413 if paramErr != nil { 414 w.Failed(ErrInvalidBody.WithCause(paramErr)) 415 return 416 } 417 result := StoreSetWithTTLResult{} 418 err := handler.store.SetWithTTL(r, param.Key, param.Value, param.TTL) 419 if err == nil { 420 } else { 421 result.Error, _ = json.Marshal(errors.Wrap(err)) 422 } 423 w.Succeed(result) 424 break 425 case "incr": 426 param := StoreIncrParam{} 427 paramErr := json.Unmarshal(cmd.Payload, ¶m) 428 if paramErr != nil { 429 w.Failed(ErrInvalidBody.WithCause(paramErr)) 430 return 431 } 432 result := StoreIncrResult{} 433 n, err := handler.store.Incr(r, param.Key, param.Delta) 434 if err == nil { 435 result.N = n 436 } else { 437 result.Error, _ = json.Marshal(errors.Wrap(err)) 438 } 439 w.Succeed(result) 440 break 441 case "remove": 442 param := StoreRemoveParam{} 443 paramErr := json.Unmarshal(cmd.Payload, ¶m) 444 if paramErr != nil { 445 w.Failed(ErrInvalidBody.WithCause(paramErr)) 446 return 447 } 448 result := StoreRemoveResult{} 449 err := handler.store.Remove(r, param.Key) 450 if err == nil { 451 } else { 452 result.Error, _ = json.Marshal(errors.Wrap(err)) 453 } 454 w.Succeed(result) 455 break 456 case "expire": 457 param := StoreExpireParam{} 458 paramErr := json.Unmarshal(cmd.Payload, ¶m) 459 if paramErr != nil { 460 w.Failed(ErrInvalidBody.WithCause(paramErr)) 461 return 462 } 463 result := StoreExpireResult{} 464 err := handler.store.Expire(r, param.Key, param.TTL) 465 if err == nil { 466 } else { 467 result.Error, _ = json.Marshal(errors.Wrap(err)) 468 } 469 w.Succeed(result) 470 break 471 default: 472 w.Failed(ErrInvalidBody) 473 return 474 } 475 }