initial commit
This commit is contained in:
193
pkg/store/postgres.go
Normal file
193
pkg/store/postgres.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"base/internal/repository/postgres/cache"
|
||||
"base/pkg/metrics"
|
||||
)
|
||||
|
||||
// PostgresStore implements Store interface using Redis
|
||||
type PostgresStore[V any] struct {
|
||||
db *gorm.DB
|
||||
logger zerolog.Logger
|
||||
metrics *metrics.Metrics
|
||||
kvTableName string
|
||||
hashTableName string
|
||||
}
|
||||
|
||||
func NewPostgresStore[V any](db *gorm.DB, logger zerolog.Logger, metrics *metrics.Metrics) Store[V] {
|
||||
return &PostgresStore[V]{
|
||||
db: db,
|
||||
logger: logger,
|
||||
metrics: metrics,
|
||||
kvTableName: cache.KVModel{}.TableName(),
|
||||
hashTableName: cache.HashModel{}.TableName(),
|
||||
}
|
||||
}
|
||||
|
||||
// Delete implements [Store].
|
||||
func (p *PostgresStore[V]) Delete(ctx context.Context, key string) error {
|
||||
err := p.db.WithContext(ctx).
|
||||
Table(p.kvTableName).
|
||||
Where("key = ?", key).
|
||||
Delete(&cache.KVModel{}).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("key not found")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("failed to delete key")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteMultiple implements [Store].
|
||||
func (p *PostgresStore[V]) DeleteMultiple(ctx context.Context, keys ...string) error {
|
||||
err := p.db.WithContext(ctx).
|
||||
Table(p.kvTableName).
|
||||
Where("key IN (?)", keys).
|
||||
Delete(&cache.KVModel{}).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
p.logger.Error().Err(err).Str("keys", strings.Join(keys, ", ")).Msg("keys not found")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Str("keys", strings.Join(keys, ", ")).Msg("failed to delete keys")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeletePattern implements [Store].
|
||||
func (p *PostgresStore[V]) DeletePattern(ctx context.Context, pattern string) error {
|
||||
err := p.db.WithContext(ctx).
|
||||
Table(p.kvTableName).
|
||||
Where("key LIKE ?", pattern).
|
||||
Delete(&cache.KVModel{}).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
p.logger.Error().Err(err).Str("pattern", pattern).Msg("pattern not found")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Str("pattern", pattern).Msg("failed to delete pattern")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Exists implements [Store].
|
||||
func (p *PostgresStore[V]) Exists(ctx context.Context, key string) (bool, error) {
|
||||
var count int64
|
||||
err := p.db.WithContext(ctx).Table(p.kvTableName).
|
||||
Where("key = ? AND (expires_at IS NULL OR expires_at > ?)", key, time.Now()).
|
||||
Count(&count).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("key not found")
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("failed to check if key exists")
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// Get implements [Store].
|
||||
func (p *PostgresStore[V]) Get(ctx context.Context, key string) (V, bool, error) {
|
||||
var row cache.KVModel
|
||||
err := p.db.WithContext(ctx).Table(p.kvTableName).
|
||||
Where("key = ? AND (expires_at IS NULL OR expires_at > ?)", key, time.Now()).
|
||||
First(&row).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
var zero V
|
||||
return zero, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
var zero V
|
||||
return zero, false, err
|
||||
}
|
||||
|
||||
var val V
|
||||
if err := json.Unmarshal(row.Value, &val); err != nil {
|
||||
return val, false, err
|
||||
}
|
||||
|
||||
return val, true, nil
|
||||
}
|
||||
|
||||
// HGetAll implements [Store].
|
||||
func (p *PostgresStore[V]) HGetAll(ctx context.Context, key string) (map[string]V, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// HMGet implements [Store].
|
||||
func (p *PostgresStore[V]) HMGet(ctx context.Context, key string, fields ...string) (map[string]V, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// HMSet implements [Store].
|
||||
func (p *PostgresStore[V]) HMSet(ctx context.Context, key string, values map[string]V, expiration time.Duration) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Set implements [Store].
|
||||
func (p *PostgresStore[V]) Set(ctx context.Context, key string, value V, expiration time.Duration) error {
|
||||
data, _ := json.Marshal(value)
|
||||
|
||||
var expires *time.Time
|
||||
if expiration > 0 {
|
||||
t := time.Now().Add(expiration)
|
||||
expires = &t
|
||||
}
|
||||
|
||||
err := p.db.WithContext(ctx).
|
||||
Table(p.kvTableName).
|
||||
Clauses(clause.OnConflict{
|
||||
UpdateAll: true,
|
||||
}).
|
||||
Create(&cache.KVModel{
|
||||
Key: key,
|
||||
Value: data,
|
||||
ExpiresAt: expires,
|
||||
}).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("key not found")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
p.logger.Error().Err(err).Str("key", key).Msg("failed to set key")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetMultiple implements [Store].
|
||||
func (p *PostgresStore[V]) SetMultiple(ctx context.Context, items map[string]V, expiration time.Duration) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SetNX implements [Store].
|
||||
func (p *PostgresStore[V]) SetNX(ctx context.Context, key string, value V, expiration time.Duration) (bool, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
Reference in New Issue
Block a user