diff --git a/session/common.go b/session/common.go index 9dee8e9..ef08602 100644 --- a/session/common.go +++ b/session/common.go @@ -1,6 +1,11 @@ package session import ( + "encoding/binary" + "encoding/hex" + "math/rand" + "time" + "go.mongodb.org/mongo-driver/bson/primitive" "repositories.action2quare.com/ayo/gocommon" ) @@ -18,3 +23,58 @@ type Authorization struct { Uid string `bson:"u" json:"u"` Email string `bson:"em" json:"em"` } + +type Provider interface { + New(*Authorization) (string, error) + Delete(primitive.ObjectID) error + Query(string) (*Authorization, error) +} + +type Consumer interface { + Query(string) (*Authorization, error) + Touch(string) (*Authorization, error) +} + +type storagekey string +type publickey string + +var r = rand.New(rand.NewSource(time.Now().UnixNano())) + +func make_storagekey(acc primitive.ObjectID) storagekey { + bs := [4]byte{} + binary.LittleEndian.PutUint32(bs[:], r.Uint32()) + + return storagekey(acc.Hex() + hex.EncodeToString(bs[2:])) +} + +func storagekey_to_publickey(sk storagekey) publickey { + bs, _ := hex.DecodeString(string(sk)) + + acc := bs[:12] + cs := bs[12:] + + encoded := [14]byte{} + for i, v := range acc[:] { + encoded[i] = (v ^ cs[0]) ^ cs[1] + } + encoded[12] = cs[0] + encoded[13] = cs[1] + + return publickey(hex.EncodeToString(encoded[:])) +} + +func publickey_to_storagekey(pk publickey) storagekey { + bs, _ := hex.DecodeString(string(pk)) + + acc := bs[:12] + cs := bs[12:] + + decoded := [14]byte{} + for i, v := range acc[:] { + decoded[i] = (v ^ cs[1]) ^ cs[0] + } + decoded[12] = cs[0] + decoded[13] = cs[1] + + return storagekey(hex.EncodeToString(decoded[:])) +} diff --git a/session/consumer_common.go b/session/consumer_common.go index f8acba1..870eb79 100644 --- a/session/consumer_common.go +++ b/session/consumer_common.go @@ -9,22 +9,17 @@ import ( ) type cache_stage[T any] struct { - cache map[string]T - deleted map[string]bool + cache map[storagekey]T + deleted map[storagekey]bool } func make_cache_stage[T any]() *cache_stage[T] { return &cache_stage[T]{ - cache: make(map[string]T), - deleted: make(map[string]bool), + cache: make(map[storagekey]T), + deleted: make(map[storagekey]bool), } } -type Consumer interface { - Query(string) (*Authorization, error) - Touch(string) (*Authorization, error) -} - type consumer_common[T any] struct { lock sync.Mutex ttl time.Duration @@ -33,36 +28,36 @@ type consumer_common[T any] struct { startTime time.Time } -func (c *consumer_common[T]) add_internal(key string, si T) { - c.stages[0].cache[key] = si - delete(c.stages[0].deleted, key) - c.stages[1].cache[key] = si - delete(c.stages[1].deleted, key) +func (c *consumer_common[T]) add_internal(sk storagekey, si T) { + c.stages[0].cache[sk] = si + delete(c.stages[0].deleted, sk) + c.stages[1].cache[sk] = si + delete(c.stages[1].deleted, sk) logger.Printf("add : %v, %v\n", *c.stages[0], *c.stages[1]) } -func (c *consumer_common[T]) add(key string, si T) { +func (c *consumer_common[T]) add(sk storagekey, si T) { c.lock.Lock() defer c.lock.Unlock() - c.add_internal(key, si) + c.add_internal(sk, si) } -func (c *consumer_common[T]) delete_internal(key string) { - delete(c.stages[0].cache, key) - c.stages[0].deleted[key] = true - delete(c.stages[1].cache, key) - c.stages[1].deleted[key] = true +func (c *consumer_common[T]) delete_internal(sk storagekey) { + delete(c.stages[0].cache, sk) + c.stages[0].deleted[sk] = true + delete(c.stages[1].cache, sk) + c.stages[1].deleted[sk] = true logger.Printf("delete : %v, %v\n", *c.stages[0], *c.stages[1]) } -func (c *consumer_common[T]) delete(key string) { +func (c *consumer_common[T]) delete(sk storagekey) { c.lock.Lock() defer c.lock.Unlock() - c.delete_internal(key) + c.delete_internal(sk) } func (c *consumer_common[T]) changeStage() { diff --git a/session/consumer_mongo.go b/session/impl_mongo.go similarity index 61% rename from session/consumer_mongo.go rename to session/impl_mongo.go index b290e1c..0c7a62c 100644 --- a/session/consumer_mongo.go +++ b/session/impl_mongo.go @@ -12,15 +12,74 @@ import ( "repositories.action2quare.com/ayo/gocommon/logger" ) +type provider_mongo struct { + mongoClient gocommon.MongoClient +} + type sessionMongo struct { - *Authorization `bson:",inline"` - Key string `bson:"key"` - Ts primitive.DateTime `bson:"_ts"` + Id primitive.ObjectID `bson:"_id,omitempty"` + Auth *Authorization `bson:"auth"` + Key storagekey `bson:"key"` + Ts primitive.DateTime `bson:"_ts"` +} + +func NewProviderWithMongo(ctx context.Context, mongoUrl string, dbname string, ttl time.Duration) (Provider, error) { + mc, err := gocommon.NewMongoClient(ctx, mongoUrl, dbname) + if err != nil { + return nil, err + } + + if err = mc.MakeUniqueIndices(session_collection_name, map[string]bson.D{ + "key": {{Key: "key", Value: 1}}, + }); err != nil { + return nil, err + } + + if err := mc.MakeExpireIndex(session_collection_name, int32(ttl.Seconds())); err != nil { + return nil, err + } + + return &provider_mongo{ + mongoClient: mc, + }, nil +} + +func (p *provider_mongo) New(input *Authorization) (string, error) { + sk := make_storagekey(input.Account) + + _, _, err := p.mongoClient.Update(session_collection_name, bson.M{ + "_id": input.Account, + }, bson.M{ + "$set": sessionMongo{ + Auth: input, + Key: sk, + Ts: primitive.NewDateTimeFromTime(time.Now().UTC()), + }, + }, options.Update().SetUpsert(true)) + + return string(storagekey_to_publickey(sk)), err +} + +func (p *provider_mongo) Delete(acc primitive.ObjectID) error { + _, err := p.mongoClient.Delete(session_collection_name, bson.M{ + "_id": acc, + }) + return err +} + +func (p *provider_mongo) Query(pk string) (*Authorization, error) { + sk := publickey_to_storagekey(publickey(pk)) + var auth Authorization + err := p.mongoClient.FindOneAs(session_collection_name, bson.M{ + "key": sk, + }, &auth) + + return &auth, err } type consumer_mongo struct { consumer_common[*sessionMongo] - ids map[primitive.ObjectID]string + ids map[primitive.ObjectID]storagekey mongoClient gocommon.MongoClient ttl time.Duration } @@ -46,7 +105,7 @@ func NewConsumerWithMongo(ctx context.Context, mongoUrl string, dbname string, t stages: [2]*cache_stage[*sessionMongo]{make_cache_stage[*sessionMongo](), make_cache_stage[*sessionMongo]()}, startTime: time.Now(), }, - ids: make(map[primitive.ObjectID]string), + ids: make(map[primitive.ObjectID]storagekey), ttl: ttl, mongoClient: mc, } @@ -100,7 +159,11 @@ func NewConsumerWithMongo(ctx context.Context, mongoUrl string, dbname string, t case "insert": consumer.add(data.Session.Key, data.DocumentKey.Id, data.Session) case "update": - consumer.add(data.Session.Key, data.DocumentKey.Id, data.Session) + if data.Session == nil { + consumer.deleteById(data.DocumentKey.Id) + } else { + consumer.add(data.Session.Key, data.DocumentKey.Id, data.Session) + } case "delete": consumer.deleteById(data.DocumentKey.Id) } @@ -134,18 +197,18 @@ func NewConsumerWithMongo(ctx context.Context, mongoUrl string, dbname string, t return consumer, nil } -func (c *consumer_mongo) query_internal(key string) (*sessionMongo, bool, error) { - if _, deleted := c.stages[0].deleted[key]; deleted { +func (c *consumer_mongo) query_internal(sk storagekey) (*sessionMongo, bool, error) { + if _, deleted := c.stages[0].deleted[sk]; deleted { return nil, false, nil } - if _, deleted := c.stages[1].deleted[key]; deleted { + if _, deleted := c.stages[1].deleted[sk]; deleted { return nil, false, nil } - found, ok := c.stages[0].cache[key] + found, ok := c.stages[0].cache[sk] if !ok { - found, ok = c.stages[1].cache[key] + found, ok = c.stages[1].cache[sk] } if ok { @@ -154,7 +217,7 @@ func (c *consumer_mongo) query_internal(key string) (*sessionMongo, bool, error) var si sessionMongo err := c.mongoClient.FindOneAs(session_collection_name, bson.M{ - "key": key, + "key": sk, }, &si) if err != nil { @@ -164,17 +227,18 @@ func (c *consumer_mongo) query_internal(key string) (*sessionMongo, bool, error) if len(si.Key) > 0 { siptr := &si - c.add_internal(key, siptr) + c.add_internal(sk, siptr) return siptr, true, nil } return nil, false, nil } -func (c *consumer_mongo) Query(key string) (*Authorization, error) { +func (c *consumer_mongo) Query(pk string) (*Authorization, error) { c.lock.Lock() defer c.lock.Unlock() - si, _, err := c.query_internal(key) + sk := publickey_to_storagekey(publickey(pk)) + si, _, err := c.query_internal(sk) if err != nil { return nil, err } @@ -187,15 +251,16 @@ func (c *consumer_mongo) Query(key string) (*Authorization, error) { return nil, nil } - return si.Authorization, nil + return si.Auth, nil } -func (c *consumer_mongo) Touch(key string) (*Authorization, error) { +func (c *consumer_mongo) Touch(pk string) (*Authorization, error) { c.lock.Lock() defer c.lock.Unlock() + sk := publickey_to_storagekey(publickey(pk)) worked, _, err := c.mongoClient.Update(session_collection_name, bson.M{ - "key": key, + "key": sk, }, bson.M{ "$currentDate": bson.M{ "_ts": bson.M{"$type": "date"}, @@ -212,7 +277,7 @@ func (c *consumer_mongo) Touch(key string) (*Authorization, error) { return nil, nil } - si, added, err := c.query_internal(key) + si, added, err := c.query_internal(sk) if err != nil { return nil, err } @@ -222,13 +287,9 @@ func (c *consumer_mongo) Touch(key string) (*Authorization, error) { } if !added { - var doc struct { - sessionMongo `bson:",inline"` - Id primitive.ObjectID `bson:"_id"` - } - + var doc sessionMongo err := c.mongoClient.FindOneAs(session_collection_name, bson.M{ - "key": key, + "key": sk, }, &doc) if err != nil { @@ -237,30 +298,30 @@ func (c *consumer_mongo) Touch(key string) (*Authorization, error) { } if len(si.Key) > 0 { - c.add_internal(key, &doc.sessionMongo) - c.ids[doc.Id] = key + c.add_internal(sk, &doc) + c.ids[doc.Id] = sk - return doc.Authorization, nil + return doc.Auth, nil } } - return si.Authorization, nil + return si.Auth, nil } -func (c *consumer_mongo) add(key string, id primitive.ObjectID, si *sessionMongo) { +func (c *consumer_mongo) add(sk storagekey, id primitive.ObjectID, si *sessionMongo) { c.lock.Lock() defer c.lock.Unlock() - c.consumer_common.add_internal(key, si) - c.ids[id] = key + c.consumer_common.add_internal(sk, si) + c.ids[id] = sk } func (c *consumer_mongo) deleteById(id primitive.ObjectID) { c.lock.Lock() defer c.lock.Unlock() - if key, ok := c.ids[id]; ok { - c.consumer_common.delete_internal(key) + if sk, ok := c.ids[id]; ok { + c.consumer_common.delete_internal(sk) delete(c.ids, id) } } diff --git a/session/consumer_redis.go b/session/impl_redis.go similarity index 53% rename from session/consumer_redis.go rename to session/impl_redis.go index 1f7f68a..f6b332f 100644 --- a/session/consumer_redis.go +++ b/session/impl_redis.go @@ -6,6 +6,7 @@ import ( "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" ) @@ -15,6 +16,77 @@ type sessionRedis struct { expireAt time.Time } +type provider_redis struct { + redisClient *redis.Client + updateChannel string + 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, + updateChannel: communication_channel_name_prefix + "_u", + deleteChannel: communication_channel_name_prefix + "_d", + ttl: ttl, + ctx: ctx, + }, nil +} + +func (p *provider_redis) New(input *Authorization) (string, error) { + bt, err := bson.Marshal(input) + if err != nil { + return "", err + } + + sk := make_storagekey(input.Account) + _, err = p.redisClient.SetEX(p.ctx, string(sk), bt, p.ttl).Result() + if err != nil { + return "", err + } + + _, err = p.redisClient.Publish(p.ctx, p.updateChannel, string(sk)).Result() + return string(storagekey_to_publickey(sk)), err +} + +func (p *provider_redis) Delete(account primitive.ObjectID) error { + prefix := account.Hex() + sks, err := p.redisClient.Keys(p.ctx, prefix+"*").Result() + if err != nil { + return err + } + + for _, sk := range sks { + p.redisClient.Del(p.ctx, sk).Result() + p.redisClient.Publish(p.ctx, p.deleteChannel, sk).Result() + } + + return nil +} + +func (p *provider_redis) Query(pk string) (*Authorization, error) { + sk := publickey_to_storagekey(publickey(pk)) + payload, err := p.redisClient.Get(p.ctx, string(sk)).Result() + if err == redis.Nil { + return nil, nil + } else if err != nil { + return nil, err + } + + var auth Authorization + if err := bson.Unmarshal([]byte(payload), &auth); err != nil { + return nil, err + } + + return &auth, nil +} + type consumer_redis struct { consumer_common[*sessionRedis] redisClient *redis.Client @@ -66,22 +138,22 @@ func NewConsumerWithRedis(ctx context.Context, redisUrl string, ttl time.Duratio switch msg.Channel { case updateChannel: - key := msg.Payload - raw, err := redisClient.Get(ctx, key).Result() + sk := storagekey(msg.Payload) + raw, err := redisClient.Get(ctx, string(sk)).Result() if err != nil { logger.Println(err) } else if len(raw) > 0 { var si Authorization if bson.Unmarshal([]byte(raw), &si) == nil { - consumer.add(key, &sessionRedis{ + consumer.add(sk, &sessionRedis{ Authorization: &si, expireAt: time.Now().Add(consumer.ttl), }) } } case deleteChannel: - key := msg.Payload - consumer.delete(key) + sk := storagekey(msg.Payload) + consumer.delete(sk) } } } @@ -90,25 +162,25 @@ func NewConsumerWithRedis(ctx context.Context, redisUrl string, ttl time.Duratio return consumer, nil } -func (c *consumer_redis) query_internal(key string) (*sessionRedis, bool, error) { - if _, deleted := c.stages[0].deleted[key]; deleted { +func (c *consumer_redis) query_internal(sk storagekey) (*sessionRedis, bool, error) { + if _, deleted := c.stages[0].deleted[sk]; deleted { return nil, false, nil } - if _, deleted := c.stages[1].deleted[key]; deleted { + if _, deleted := c.stages[1].deleted[sk]; deleted { return nil, false, nil } - found, ok := c.stages[0].cache[key] + found, ok := c.stages[0].cache[sk] if !ok { - found, ok = c.stages[1].cache[key] + found, ok = c.stages[1].cache[sk] } if ok { return found, false, nil } - payload, err := c.redisClient.Get(c.ctx, key).Result() + payload, err := c.redisClient.Get(c.ctx, string(sk)).Result() if err == redis.Nil { return nil, false, nil } else if err != nil { @@ -125,7 +197,7 @@ func (c *consumer_redis) query_internal(key string) (*sessionRedis, bool, error) return nil, false, err } - ttl, err := c.redisClient.TTL(c.ctx, key).Result() + ttl, err := c.redisClient.TTL(c.ctx, string(sk)).Result() if err != nil { logger.Println("consumer Query :", err) return nil, false, err @@ -135,16 +207,17 @@ func (c *consumer_redis) query_internal(key string) (*sessionRedis, bool, error) Authorization: &auth, expireAt: time.Now().Add(ttl), } - c.add_internal(key, si) + c.add_internal(sk, si) return si, true, nil } -func (c *consumer_redis) Query(key string) (*Authorization, error) { +func (c *consumer_redis) Query(pk string) (*Authorization, error) { c.lock.Lock() defer c.lock.Unlock() - si, _, err := c.query_internal(key) + sk := publickey_to_storagekey(publickey(pk)) + si, _, err := c.query_internal(sk) if err != nil { return nil, err } @@ -160,11 +233,12 @@ func (c *consumer_redis) Query(key string) (*Authorization, error) { return si.Authorization, nil } -func (c *consumer_redis) Touch(key string) (*Authorization, error) { +func (c *consumer_redis) Touch(pk string) (*Authorization, error) { c.lock.Lock() defer c.lock.Unlock() - ok, err := c.redisClient.Expire(c.ctx, key, c.ttl).Result() + sk := publickey_to_storagekey(publickey(pk)) + ok, err := c.redisClient.Expire(c.ctx, string(sk), c.ttl).Result() if err == redis.Nil { return nil, nil } else if err != nil { @@ -174,7 +248,7 @@ func (c *consumer_redis) Touch(key string) (*Authorization, error) { if ok { // redis에 살아있다. - si, added, err := c.query_internal(key) + si, added, err := c.query_internal(sk) if err != nil { return nil, err } @@ -186,7 +260,7 @@ func (c *consumer_redis) Touch(key string) (*Authorization, error) { if !added { si.expireAt = time.Now().Add(c.ttl) // stage 0으로 옮기기 위해 add_internal을 다시 부름 - c.add_internal(key, si) + c.add_internal(sk, si) } return si.Authorization, nil diff --git a/session/provider.go b/session/provider.go deleted file mode 100644 index 0a387ad..0000000 --- a/session/provider.go +++ /dev/null @@ -1,138 +0,0 @@ -package session - -import ( - "context" - "time" - - "github.com/go-redis/redis/v8" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/options" - "repositories.action2quare.com/ayo/gocommon" -) - -type Provider interface { - Update(string, *Authorization) error - Delete(string) error - Query(string) (*Authorization, error) -} - -type provider_redis struct { - redisClient *redis.Client - updateChannel string - deleteChannel string - ttl time.Duration - ctx context.Context -} - -type provider_mongo struct { - mongoClient gocommon.MongoClient -} - -func NewProviderWithMongo(ctx context.Context, mongoUrl string, dbname string, ttl time.Duration) (Provider, error) { - mc, err := gocommon.NewMongoClient(ctx, mongoUrl, dbname) - if err != nil { - return nil, err - } - - if err = mc.MakeUniqueIndices(session_collection_name, map[string]bson.D{ - "key": {{Key: "key", Value: 1}}, - }); err != nil { - return nil, err - } - - if err := mc.MakeExpireIndex(session_collection_name, int32(ttl.Seconds())); err != nil { - return nil, err - } - - return &provider_mongo{ - mongoClient: mc, - }, nil -} - -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, - updateChannel: communication_channel_name_prefix + "_u", - deleteChannel: communication_channel_name_prefix + "_d", - ttl: ttl, - ctx: ctx, - }, nil -} - -func (p *provider_redis) Update(key string, input *Authorization) error { - bt, err := bson.Marshal(input) - if err != nil { - return err - } - - _, err = p.redisClient.SetEX(p.ctx, key, bt, p.ttl).Result() - if err != nil { - return err - } - - _, err = p.redisClient.Publish(p.ctx, p.updateChannel, key).Result() - return err -} - -func (p *provider_redis) Delete(key string) error { - cnt, err := p.redisClient.Del(p.ctx, key).Result() - if err != nil { - return err - } - - if cnt > 0 { - _, err = p.redisClient.Publish(p.ctx, p.deleteChannel, key).Result() - } - - return err -} - -func (p *provider_redis) Query(key string) (*Authorization, error) { - payload, err := p.redisClient.Get(p.ctx, key).Result() - if err == redis.Nil { - return nil, nil - } else if err != nil { - return nil, err - } - - var auth Authorization - if err := bson.Unmarshal([]byte(payload), &auth); err != nil { - return nil, err - } - - return &auth, nil -} - -func (p *provider_mongo) Update(key string, input *Authorization) error { - _, _, err := p.mongoClient.Update(session_collection_name, bson.M{ - "key": key, - }, bson.M{ - "$set": input, - "$currentDate": bson.M{ - "_ts": bson.M{"$type": "date"}, - }, - }, options.Update().SetUpsert(true)) - - return err -} - -func (p *provider_mongo) Delete(key string) error { - _, err := p.mongoClient.Delete(session_collection_name, bson.M{ - "key": key, - }) - return err -} - -func (p *provider_mongo) Query(key string) (*Authorization, error) { - var auth Authorization - err := p.mongoClient.FindOneAs(session_collection_name, bson.M{ - "key": key, - }, &auth) - - return &auth, err -} diff --git a/session/session_test.go b/session/session_test.go index 570b3ae..4e82e27 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -11,28 +11,53 @@ import ( ) func TestExpTable(t *testing.T) { - // pv, err := NewProviderWithRedis(context.Background(), "redis://192.168.8.94:6380/1", 10*time.Second) - // if err != nil { - // t.Error(err) - // } - - // cs, err := NewConsumerWithRedis(context.Background(), "redis://192.168.8.94:6380/1", 10*time.Second) - // if err != nil { - // t.Error(err) - // } - - pv, err := NewProviderWithMongo(context.Background(), "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", "maingate", 10*time.Second) + pv, err := NewProviderWithRedis(context.Background(), "redis://192.168.8.94:6380/1", 10*time.Second) if err != nil { t.Error(err) } - cs, err := NewConsumerWithMongo(context.Background(), "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", "maingate", 10*time.Second) + cs, err := NewConsumerWithRedis(context.Background(), "redis://192.168.8.94:6380/1", 10*time.Second) + if err != nil { + t.Error(err) + } + + // pv, err := NewProviderWithMongo(context.Background(), "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", "maingate", 10*time.Second) + // if err != nil { + // t.Error(err) + // } + + // cs, err := NewConsumerWithMongo(context.Background(), "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", "maingate", 10*time.Second) + // if err != nil { + // t.Error(err) + // } + + test := primitive.NewObjectID() + sk := make_storagekey(test) + pk := storagekey_to_publickey(sk) + if publickey_to_storagekey(pk) != sk { + t.Errorf("pk != sk : %s, %s", pk, sk) + } + + au1 := &Authorization{ + Account: primitive.NewObjectID(), + Platform: "editor", + Uid: "uid-1", + } + sk1, err := pv.New(au1) + if err != nil { + t.Error(err) + } + + au2 := &Authorization{ + Account: primitive.NewObjectID(), + Platform: "editor", + Uid: "uid-2", + } + sk2, err := pv.New(au2) if err != nil { t.Error(err) } - sk1 := primitive.NewObjectID().Hex() - sk2 := primitive.NewObjectID().Hex() go func() { for { q1, err := cs.Query(sk1) @@ -44,27 +69,13 @@ func TestExpTable(t *testing.T) { } }() - time.Sleep(2 * time.Second) - pv.Update(sk1, &Authorization{ - Account: primitive.NewObjectID(), - Platform: "editor", - Uid: "uid-1", - }) - - time.Sleep(2 * time.Second) - pv.Update(sk2, &Authorization{ - Account: primitive.NewObjectID(), - Platform: "editor", - Uid: "uid-2", - }) - cs.Touch(sk1) time.Sleep(2 * time.Second) cs.Touch(sk2) time.Sleep(2 * time.Second) time.Sleep(2 * time.Second) - pv.Delete(sk1) + pv.Delete(au1.Account) cs.Touch(sk1) time.Sleep(2 * time.Second)