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, &param)
   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, &param)
   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, &param)
   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, &param)
   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, &param)
   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, &param)
   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  }