2020-12-06 11:36:04 +00:00
|
|
|
package bbgo
|
|
|
|
|
2021-02-20 16:45:56 +00:00
|
|
|
import (
|
2022-10-03 10:45:24 +00:00
|
|
|
"context"
|
2022-10-05 10:42:31 +00:00
|
|
|
"os"
|
2022-05-05 04:53:48 +00:00
|
|
|
"reflect"
|
2023-04-26 09:40:01 +00:00
|
|
|
"sync"
|
2021-02-20 16:45:56 +00:00
|
|
|
|
2022-10-05 10:42:31 +00:00
|
|
|
"github.com/codingconcepts/env"
|
2022-10-05 10:46:26 +00:00
|
|
|
"github.com/pkg/errors"
|
2022-01-21 15:28:51 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2022-03-14 13:21:04 +00:00
|
|
|
|
2022-06-29 10:49:42 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/dynamic"
|
2022-03-14 13:21:04 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/service"
|
2021-02-20 16:45:56 +00:00
|
|
|
)
|
2020-12-07 03:43:17 +00:00
|
|
|
|
2022-10-04 09:23:43 +00:00
|
|
|
var defaultPersistenceServiceFacade = &service.PersistenceServiceFacade{
|
2022-06-20 17:05:13 +00:00
|
|
|
Memory: service.NewMemoryService(),
|
|
|
|
}
|
|
|
|
|
2022-06-21 06:32:43 +00:00
|
|
|
// Sync syncs the object properties into the persistence layer
|
2022-10-03 10:45:24 +00:00
|
|
|
func Sync(ctx context.Context, obj interface{}) {
|
2022-08-22 18:12:26 +00:00
|
|
|
id := dynamic.CallID(obj)
|
2022-06-21 06:32:43 +00:00
|
|
|
if len(id) == 0 {
|
2022-06-21 07:57:26 +00:00
|
|
|
log.Warnf("InstanceID() is not provided, can not sync persistence")
|
|
|
|
return
|
2022-06-21 06:32:43 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 09:23:43 +00:00
|
|
|
isolation := GetIsolationFromContext(ctx)
|
|
|
|
|
|
|
|
ps := isolation.persistenceServiceFacade.Get()
|
2023-04-26 09:40:01 +00:00
|
|
|
|
|
|
|
locker, ok := obj.(sync.Locker)
|
|
|
|
if ok {
|
|
|
|
locker.Lock()
|
|
|
|
defer locker.Unlock()
|
|
|
|
}
|
|
|
|
|
2022-06-21 07:57:26 +00:00
|
|
|
err := storePersistenceFields(obj, id, ps)
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Errorf("persistence sync failed")
|
|
|
|
}
|
2022-05-05 10:02:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 05:05:01 +00:00
|
|
|
func loadPersistenceFields(obj interface{}, id string, persistence service.PersistenceService) error {
|
2022-06-29 10:49:42 +00:00
|
|
|
return dynamic.IterateFieldsByTag(obj, "persistence", func(tag string, field reflect.StructField, value reflect.Value) error {
|
2022-06-02 19:10:50 +00:00
|
|
|
log.Debugf("[loadPersistenceFields] loading value into field %v, tag = %s, original value = %v", field, tag, value)
|
|
|
|
|
2022-06-29 10:49:42 +00:00
|
|
|
newValueInf := dynamic.NewTypeValueInterface(value.Type())
|
2022-05-05 04:53:48 +00:00
|
|
|
// inf := value.Interface()
|
2022-05-05 09:56:41 +00:00
|
|
|
store := persistence.NewStore("state", id, tag)
|
2022-05-05 04:53:48 +00:00
|
|
|
if err := store.Load(&newValueInf); err != nil {
|
2022-05-05 06:04:44 +00:00
|
|
|
if err == service.ErrPersistenceNotExists {
|
2022-06-02 19:10:50 +00:00
|
|
|
log.Debugf("[loadPersistenceFields] state key does not exist, id = %v, tag = %s", id, tag)
|
2022-05-05 06:04:44 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-05 04:53:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
newValue := reflect.ValueOf(newValueInf)
|
|
|
|
if value.Kind() != reflect.Ptr && newValue.Kind() == reflect.Ptr {
|
|
|
|
newValue = newValue.Elem()
|
|
|
|
}
|
|
|
|
|
2022-06-04 17:09:31 +00:00
|
|
|
log.Debugf("[loadPersistenceFields] %v = %v -> %v\n", field, value, newValue)
|
2022-05-05 04:53:48 +00:00
|
|
|
|
|
|
|
value.Set(newValue)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-05-05 05:05:01 +00:00
|
|
|
func storePersistenceFields(obj interface{}, id string, persistence service.PersistenceService) error {
|
2022-06-29 10:49:42 +00:00
|
|
|
return dynamic.IterateFieldsByTag(obj, "persistence", func(tag string, ft reflect.StructField, fv reflect.Value) error {
|
2022-06-02 19:10:50 +00:00
|
|
|
log.Debugf("[storePersistenceFields] storing value from field %v, tag = %s, original value = %v", ft, tag, fv)
|
2022-05-05 04:53:48 +00:00
|
|
|
|
2022-06-02 19:10:50 +00:00
|
|
|
inf := fv.Interface()
|
2022-05-05 09:56:41 +00:00
|
|
|
store := persistence.NewStore("state", id, tag)
|
2022-05-05 05:16:46 +00:00
|
|
|
return store.Save(inf)
|
2022-05-05 04:53:48 +00:00
|
|
|
})
|
|
|
|
}
|
2022-10-05 10:42:31 +00:00
|
|
|
|
2022-10-05 10:46:26 +00:00
|
|
|
func NewPersistenceServiceFacade(conf *PersistenceConfig) (*service.PersistenceServiceFacade, error) {
|
|
|
|
facade := &service.PersistenceServiceFacade{
|
|
|
|
Memory: service.NewMemoryService(),
|
|
|
|
}
|
|
|
|
|
2022-10-05 10:42:31 +00:00
|
|
|
if conf.Redis != nil {
|
|
|
|
if err := env.Set(conf.Redis); err != nil {
|
2022-10-05 10:46:26 +00:00
|
|
|
return nil, err
|
2022-10-05 10:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
redisPersistence := service.NewRedisPersistenceService(conf.Redis)
|
2022-10-05 10:46:26 +00:00
|
|
|
facade.Redis = redisPersistence
|
2022-10-05 10:42:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if conf.Json != nil {
|
|
|
|
if _, err := os.Stat(conf.Json.Directory); os.IsNotExist(err) {
|
|
|
|
if err2 := os.MkdirAll(conf.Json.Directory, 0777); err2 != nil {
|
2022-10-05 10:46:26 +00:00
|
|
|
return nil, errors.Wrapf(err2, "can not create directory: %s", conf.Json.Directory)
|
2022-10-05 10:42:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonPersistence := &service.JsonPersistenceService{Directory: conf.Json.Directory}
|
2022-10-05 10:46:26 +00:00
|
|
|
facade.Json = jsonPersistence
|
|
|
|
}
|
|
|
|
|
|
|
|
return facade, nil
|
|
|
|
}
|
|
|
|
|
2023-03-02 14:33:33 +00:00
|
|
|
func ConfigurePersistence(ctx context.Context, environ *Environment, conf *PersistenceConfig) error {
|
2022-10-05 10:46:26 +00:00
|
|
|
facade, err := NewPersistenceServiceFacade(conf)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-10-05 10:42:31 +00:00
|
|
|
}
|
|
|
|
|
2022-10-05 10:48:12 +00:00
|
|
|
isolation := GetIsolationFromContext(ctx)
|
|
|
|
isolation.persistenceServiceFacade = facade
|
|
|
|
|
2023-03-02 14:33:33 +00:00
|
|
|
environ.PersistentService = facade
|
2022-10-05 10:42:31 +00:00
|
|
|
return nil
|
2023-04-25 16:37:13 +00:00
|
|
|
}
|