package caching import ( "context" "encoding/json" "fmt" "path" "time" "github.com/pkg/errors" "github.com/ydb-platform/ydb-go-sdk/v3/table" "github.com/ydb-platform/ydb-go-sdk/v3/table/options" "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named" "github.com/ydb-platform/ydb-go-sdk/v3/table/types" "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" ) type CacheClient struct { Storage table.Client Expiration time.Duration Prefix string } func (client *CacheClient) Init(ctx context.Context) error { return client.Storage.Do(ctx, func(ctx context.Context, s table.Session) error { return s.CreateTable( ctx, path.Join(client.Prefix, "cache"), options.WithColumn("card", types.TypeString), options.WithColumn("prices", types.Optional(types.TypeJSON)), options.WithColumn("created_at", types.Optional(types.TypeTimestamp)), options.WithTimeToLiveSettings( options.NewTTLSettings().ColumnDateType("created_at").ExpireAfter(client.Expiration), ), options.WithPrimaryKeyColumn("card"), ) }) } func (client *CacheClient) Set(ctx context.Context, key string, prices []cardsinfo.ScgCardPrice) error { const query = ` DECLARE $cacheData AS List>; INSERT INTO cache SELECT cd.card AS card, cd.prices AS prices, cd.created_at AS created_at FROM AS_TABLE($cacheData) cd LEFT OUTER JOIN cache c ON cd.card = c.card WHERE c.card IS NULL` value, _ := json.Marshal(prices) return client.Storage.Do(ctx, func(ctx context.Context, s table.Session) error { _, _, err := s.Execute(ctx, writeTx(), query, table.NewQueryParameters( table.ValueParam("$cacheData", types.ListValue( types.StructValue( types.StructFieldValue("card", types.StringValueFromString(key)), types.StructFieldValue("prices", types.JSONValueFromBytes(value)), types.StructFieldValue("created_at", types.TimestampValueFromTime(time.Now())), ))), )) return err }) } func (client *CacheClient) Get(ctx context.Context, key string) ([]cardsinfo.ScgCardPrice, error) { const query = ` DECLARE $card AS String; SELECT UNWRAP(prices) AS prices FROM cache WHERE card = $card` var pricesStr string err := client.Storage.Do(ctx, func(ctx context.Context, s table.Session) error { _, res, err := s.Execute(ctx, readTx(), query, table.NewQueryParameters( table.ValueParam("$card", types.StringValueFromString(key)), )) if err != nil { return err } ok := res.NextResultSet(ctx) if !ok { return errors.New("no key") } ok = res.NextRow() if !ok { return errors.New("no key") } err = res.ScanNamed( named.Required("prices", &pricesStr), ) return err }) if err != nil { fmt.Println(err.Error()) return nil, errors.Wrap(err, "Failed to get key") } var prices []cardsinfo.ScgCardPrice json.Unmarshal([]byte(pricesStr), &prices) return prices, nil } func writeTx() *table.TransactionControl { return table.TxControl(table.BeginTx( table.WithSerializableReadWrite(), ), table.CommitTx()) } func readTx() *table.TransactionControl { return table.TxControl(table.BeginTx( table.WithOnlineReadOnly(), ), table.CommitTx()) }