Files
gocommon/session/impl_redis.go

368 lines
8.1 KiB
Go
Raw Normal View History

2023-08-30 13:15:44 +09:00
package session
import (
"context"
"fmt"
2023-08-30 13:15:44 +09:00
"time"
"github.com/go-redis/redis/v8"
"go.mongodb.org/mongo-driver/bson/primitive"
2023-08-30 13:15:44 +09:00
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger"
)
const (
communication_channel_name_prefix = "_sess_comm_chan_name"
)
2023-08-30 15:43:26 +09:00
type sessionRedis struct {
*Authorization
2023-08-30 13:15:44 +09:00
expireAt time.Time
}
type provider_redis struct {
redisClient *redis.Client
deleteChannel string
ttl time.Duration
ctx context.Context
}
func newProviderWithRedis(ctx context.Context, redisUrl string, ttl time.Duration) (Provider, error) {
redisClient, err := gocommon.NewRedisClient(redisUrl)
if err != nil {
return nil, err
}
return &provider_redis{
redisClient: redisClient,
2023-12-25 22:23:46 +09:00
deleteChannel: fmt.Sprintf("%s_%d_d", communication_channel_name_prefix, redisClient.Options().DB),
ttl: ttl,
ctx: ctx,
}, nil
}
func (p *provider_redis) New(input *Authorization) (string, error) {
2024-04-22 18:05:43 +09:00
newsk := make_storagekey(input.Account)
prefix := input.Account.Hex()
sks, err := p.redisClient.Keys(p.ctx, prefix+"*").Result()
if err != nil {
logger.Println("session provider delete :", sks, err)
return "", err
}
2024-04-23 15:57:25 +09:00
p.redisClient.Del(p.ctx, sks...)
2024-04-22 18:05:43 +09:00
for _, sk := range sks {
p.redisClient.Publish(p.ctx, p.deleteChannel, sk).Result()
}
for {
duplicated := false
for _, sk := range sks {
if sk == string(newsk) {
duplicated = true
break
}
}
if !duplicated {
break
}
newsk = make_storagekey(input.Account)
}
_, err = p.redisClient.HSet(p.ctx, string(newsk), input.ToStrings()).Result()
if err != nil {
return "", err
}
2024-04-22 18:05:43 +09:00
_, err = p.redisClient.Expire(p.ctx, string(newsk), p.ttl).Result()
2024-02-21 12:49:38 +09:00
if err != nil {
return "", err
}
2024-04-22 18:05:43 +09:00
pk := storagekey_to_publickey(newsk)
2023-09-19 18:50:17 +09:00
return string(pk), err
}
func (p *provider_redis) RevokeAll(account primitive.ObjectID) error {
prefix := account.Hex()
sks, err := p.redisClient.Keys(p.ctx, prefix+"*").Result()
if err != nil {
2023-09-19 18:50:17 +09:00
logger.Println("session provider delete :", sks, err)
return err
}
for _, sk := range sks {
2024-02-21 12:08:33 +09:00
p.redisClient.HSet(p.ctx, sk, "inv", "true")
p.redisClient.Publish(p.ctx, p.deleteChannel, sk).Result()
}
return nil
}
2023-09-04 14:35:33 +09:00
func (p *provider_redis) Query(pk string) (Authorization, error) {
sk := publickey_to_storagekey(publickey(pk))
2024-02-21 12:08:33 +09:00
src, err := p.redisClient.HGetAll(p.ctx, string(sk)).Result()
if err == redis.Nil {
2023-09-19 18:50:17 +09:00
logger.Println("session provider query :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
} else if err != nil {
2023-09-19 18:50:17 +09:00
logger.Println("session provider query :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, err
}
2024-02-21 12:08:33 +09:00
auth := MakeAuthrizationFromStringMap(src)
2023-09-04 14:35:33 +09:00
return auth, nil
}
2023-09-04 11:56:56 +09:00
func (p *provider_redis) Touch(pk string) (bool, error) {
sk := publickey_to_storagekey(publickey(pk))
ok, err := p.redisClient.Expire(p.ctx, string(sk), p.ttl).Result()
2023-09-19 18:50:17 +09:00
2023-09-04 11:56:56 +09:00
if err == redis.Nil {
// 이미 만료됨
2023-12-28 16:55:06 +09:00
logger.Println("session provider touch :", pk, err)
2023-09-04 11:56:56 +09:00
return false, nil
} else if err != nil {
2023-12-28 16:55:06 +09:00
logger.Println("session provider touch :", pk, err)
2023-09-04 11:56:56 +09:00
return false, err
}
return ok, nil
}
2023-08-30 15:43:26 +09:00
type consumer_redis struct {
consumer_common[*sessionRedis]
redisClient *redis.Client
deleteChannel string
2023-08-30 13:15:44 +09:00
}
func newConsumerWithRedis(ctx context.Context, redisUrl string, ttl time.Duration) (Consumer, error) {
2023-08-30 13:15:44 +09:00
redisClient, err := gocommon.NewRedisClient(redisUrl)
if err != nil {
return nil, err
}
deleteChannel := fmt.Sprintf("%s_%d_d", communication_channel_name_prefix, redisClient.Options().DB)
sub := redisClient.Subscribe(ctx, deleteChannel)
2023-08-30 15:43:26 +09:00
consumer := &consumer_redis{
consumer_common: consumer_common[*sessionRedis]{
2023-08-30 15:43:26 +09:00
ttl: ttl,
ctx: ctx,
stages: [2]*cache_stage[*sessionRedis]{make_cache_stage[*sessionRedis](), make_cache_stage[*sessionRedis]()},
2023-08-30 15:43:26 +09:00
startTime: time.Now(),
},
redisClient: redisClient,
deleteChannel: deleteChannel,
2023-08-30 13:15:44 +09:00
}
go func() {
stageswitch := time.Now().Add(ttl)
tickTimer := time.After(ttl)
for {
select {
case <-ctx.Done():
return
case <-tickTimer:
consumer.changeStage()
stageswitch = stageswitch.Add(ttl)
tempttl := time.Until(stageswitch)
tickTimer = time.After(tempttl)
case msg := <-sub.Channel():
if msg == nil {
return
}
if len(msg.Payload) == 0 {
continue
}
switch msg.Channel {
case deleteChannel:
sk := storagekey(msg.Payload)
2023-12-25 22:06:57 +09:00
old := consumer.delete(sk)
if old != nil {
for _, f := range consumer.onSessionInvalidated {
f(old.Account)
}
}
2023-08-30 13:15:44 +09:00
}
}
}
}()
return consumer, nil
}
2024-02-21 12:08:33 +09:00
func (c *consumer_redis) query_internal(sk storagekey) (*sessionRedis, error) {
if old, deleted := c.stages[0].deleted[sk]; deleted {
return old, nil
2023-08-30 13:15:44 +09:00
}
2024-02-21 12:08:33 +09:00
if old, deleted := c.stages[1].deleted[sk]; deleted {
return old, nil
2023-08-30 13:15:44 +09:00
}
found, ok := c.stages[0].cache[sk]
2023-08-30 13:15:44 +09:00
if !ok {
found, ok = c.stages[1].cache[sk]
2023-08-30 13:15:44 +09:00
}
if ok {
2023-09-06 12:41:49 +09:00
if time.Now().Before(found.expireAt) {
// 만료전 세션
2024-02-21 12:08:33 +09:00
return found, nil
2023-09-06 12:41:49 +09:00
}
// 다른 Consumer가 Touch했을 수도 있으므로 redis에서 읽어본다.
2023-08-30 13:15:44 +09:00
}
2024-02-21 12:08:33 +09:00
payload, err := c.redisClient.HGetAll(c.ctx, string(sk)).Result()
2023-09-06 12:41:49 +09:00
if err != nil && err != redis.Nil {
2023-08-30 13:15:44 +09:00
logger.Println("consumer Query :", err)
2024-02-21 12:08:33 +09:00
return nil, err
2023-08-30 13:15:44 +09:00
}
if len(payload) == 0 {
2024-02-21 12:08:33 +09:00
return nil, nil
}
ttl, err := c.redisClient.TTL(c.ctx, string(sk)).Result()
if err != nil {
logger.Println("consumer Query :", err)
2024-02-21 12:08:33 +09:00
return nil, err
}
2024-02-21 12:49:38 +09:00
if ttl < 0 {
ttl = time.Duration(time.Hour * 24)
}
2024-02-21 12:08:33 +09:00
auth := MakeAuthrizationFromStringMap(payload)
si := &sessionRedis{
Authorization: &auth,
expireAt: time.Now().Add(ttl),
}
2024-02-21 12:08:33 +09:00
if auth.Invalidated() {
c.stages[0].deleted[sk] = si
} else {
c.add_internal(sk, si)
}
return si, nil
}
2023-09-04 14:35:33 +09:00
func (c *consumer_redis) Query(pk string) (Authorization, error) {
c.lock.Lock()
defer c.lock.Unlock()
sk := publickey_to_storagekey(publickey(pk))
2025-08-15 13:23:04 +09:00
if _, deleted := c.stages[0].deleted[sk]; deleted {
return Authorization{}, nil
}
if _, deleted := c.stages[1].deleted[sk]; deleted {
return Authorization{}, nil
}
2024-02-21 12:08:33 +09:00
si, err := c.query_internal(sk)
if err != nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer query :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, err
}
if si == nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer query(si nil) :", pk, nil)
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
2023-08-30 13:15:44 +09:00
}
if time.Now().After(si.expireAt) {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer query(expired):", pk, nil)
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
}
2023-09-04 14:35:33 +09:00
return *si.Authorization, nil
2023-08-30 13:15:44 +09:00
}
2023-09-04 14:35:33 +09:00
func (c *consumer_redis) Touch(pk string) (Authorization, error) {
2023-08-30 13:15:44 +09:00
c.lock.Lock()
defer c.lock.Unlock()
sk := publickey_to_storagekey(publickey(pk))
2023-12-28 16:55:06 +09:00
if _, deleted := c.stages[0].deleted[sk]; deleted {
return Authorization{}, nil
}
if _, deleted := c.stages[1].deleted[sk]; deleted {
return Authorization{}, nil
}
ok, err := c.redisClient.Expire(c.ctx, string(sk), c.ttl).Result()
2023-08-30 13:15:44 +09:00
if err == redis.Nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer touch :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
2023-08-30 13:15:44 +09:00
} else if err != nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer touch :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, err
2023-08-30 13:15:44 +09:00
}
if ok {
// redis에 살아있다.
2024-02-21 12:08:33 +09:00
si, err := c.query_internal(sk)
if err != nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer touch(ok) :", pk, err)
2023-09-04 14:35:33 +09:00
return Authorization{}, err
2023-08-30 13:15:44 +09:00
}
if si == nil {
2023-09-19 18:50:17 +09:00
logger.Println("session consumer touch(ok, si nil) :", pk)
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
}
2023-09-04 14:35:33 +09:00
return *si.Authorization, nil
2023-08-30 13:15:44 +09:00
}
2023-09-04 14:35:33 +09:00
return Authorization{}, nil
2023-08-30 13:15:44 +09:00
}
2023-12-25 22:06:57 +09:00
func (c *consumer_redis) Revoke(pk string) {
sk := publickey_to_storagekey(publickey(pk))
c.redisClient.Del(c.ctx, string(sk))
2024-04-23 15:57:25 +09:00
c.lock.Lock()
defer c.lock.Unlock()
if sr, ok := c.stages[0].cache[sk]; ok {
c.stages[0].deleted[sk] = sr
}
if sr, ok := c.stages[1].cache[sk]; ok {
c.stages[1].deleted[sk] = sr
}
}
func (c *consumer_redis) IsRevoked(accid primitive.ObjectID) bool {
2024-02-21 12:08:33 +09:00
sk := make_storagekey(accid)
c.lock.Lock()
defer c.lock.Unlock()
if _, deleted := c.stages[0].deleted[sk]; deleted {
return true
}
if _, deleted := c.stages[1].deleted[sk]; deleted {
return true
}
return false
}
2023-12-25 22:06:57 +09:00
func (c *consumer_redis) RegisterOnSessionInvalidated(cb func(primitive.ObjectID)) {
c.onSessionInvalidated = append(c.onSessionInvalidated, cb)
}