InMemory 그룹을 redis로 변경
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
common "repositories.action2quare.com/ayo/gocommon"
|
||||
@ -84,7 +83,7 @@ func (sub *subTavern) JoinGroup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
writeBsonDoc(w, map[string]string{
|
||||
"gid": gidobj.Hex(),
|
||||
"tid": tidobj.Hex(),
|
||||
})
|
||||
@ -195,9 +194,6 @@ func (sub *subTavern) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO : full group doc을 내려보냄
|
||||
// w.Write([]byte(gidbytes.Hex()))
|
||||
}
|
||||
|
||||
func (sub *subTavern) DenyInvitation(w http.ResponseWriter, r *http.Request) {
|
||||
@ -292,7 +288,7 @@ func (sub *subTavern) SearchGroup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := writeBsonArr(w, result); err != nil {
|
||||
logger.Error("json marshal failed :", err)
|
||||
logger.Error("bson marshal failed :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -369,7 +365,7 @@ func (sub *subTavern) QueryGroup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := writeBsonDoc(w, result); err != nil {
|
||||
logger.Error("json marshal failed :", err)
|
||||
logger.Error("bson marshal failed :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -486,8 +482,7 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
var frag bson.M
|
||||
dec := json.NewDecoder(r.Body)
|
||||
if err := dec.Decode(&frag); err != nil {
|
||||
if err := readBsonDoc(r.Body, &frag); err != nil {
|
||||
logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
@ -499,3 +494,33 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (sub *subTavern) QueryGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
typename, _ := common.ReadStringFormValue(r.Form, "type")
|
||||
group := sub.groups[typename]
|
||||
if group == nil {
|
||||
logger.Println("QueryGroupMembers failed. type is missing")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
gid, ok := common.ReadObjectIDFormValue(r.Form, "gid")
|
||||
if !ok {
|
||||
logger.Println("QueryGroupMembers failed. gid is missing")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
members, err := group.QueryGroupMembers(gid)
|
||||
if err != nil {
|
||||
logger.Error("QueryGroupMembers failed. group.QueryGroupMembers returns err :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := writeBsonDoc(w, members); err != nil {
|
||||
logger.Error("QueryGroupMembers failed. writeBsonDoc return err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,4 +38,5 @@ type group interface {
|
||||
UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error
|
||||
Dismiss(groupID primitive.ObjectID) error
|
||||
UpdateGroupDocument(groupID primitive.ObjectID, doc bson.M) error
|
||||
QueryGroupMembers(groupID primitive.ObjectID) (bson.M, error)
|
||||
}
|
||||
|
||||
@ -54,36 +54,45 @@ type memberDoc struct {
|
||||
type InvitationFail bson.M
|
||||
|
||||
type groupDoc struct {
|
||||
Body bson.M `json:"_body"`
|
||||
Members map[string]*memberDoc `json:"_members"`
|
||||
Members map[string]any `json:"_members"`
|
||||
InCharge string `json:"_incharge"`
|
||||
Gid string `json:"_gid"`
|
||||
|
||||
rh *RedisonHandler
|
||||
rh *gocommon.RedisonHandler
|
||||
id groupID
|
||||
}
|
||||
|
||||
func (p groupDoc) MarshalJSON() ([]byte, error) {
|
||||
if len(p.Gid) == 0 {
|
||||
p.Gid = p.id.Hex()
|
||||
func (gd *groupDoc) loadMemberFull(tid string) (bson.M, error) {
|
||||
full, err := gd.rh.JSONGet(gd.strid(), "$._members."+tid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Turn p into a map
|
||||
type groupDoc_ groupDoc // prevent recursion
|
||||
b, _ := json.Marshal(groupDoc_(p))
|
||||
bt := []byte(full.(string))
|
||||
bt = bt[1 : len(bt)-1]
|
||||
|
||||
var m map[string]json.RawMessage
|
||||
_ = json.Unmarshal(b, &m)
|
||||
|
||||
// Add tags to the map, possibly overriding struct fields
|
||||
for k, v := range p.Body {
|
||||
// if overriding struct fields is not acceptable:
|
||||
// if _, ok := m[k]; ok { continue }
|
||||
b, _ = json.Marshal(v)
|
||||
m[k] = b
|
||||
var doc bson.M
|
||||
if err = json.Unmarshal(bt, &doc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func (gd *groupDoc) loadFull() (doc bson.M) {
|
||||
// 새 멤버에 그룹 전체를 알림
|
||||
full, err := gd.rh.JSONGet(gd.strid(), "$")
|
||||
if err == nil {
|
||||
bt := []byte(full.(string))
|
||||
bt = bt[1 : len(bt)-1]
|
||||
err = json.Unmarshal(bt, &doc)
|
||||
if err != nil {
|
||||
logger.Println("loadFull err :", err)
|
||||
}
|
||||
} else {
|
||||
logger.Println("loadFull err :", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (gd *groupDoc) strid() string {
|
||||
@ -160,18 +169,19 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
|
||||
return newdoc, err
|
||||
}
|
||||
|
||||
func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) {
|
||||
memdoc := &memberDoc{
|
||||
Body: doc,
|
||||
Invite: false,
|
||||
InviteExpire: 0,
|
||||
}
|
||||
func (gd *groupDoc) addMember(mid accountID, doc bson.M) (bson.M, error) {
|
||||
tid := gd.tid(mid)
|
||||
prefix := "$._members." + tid
|
||||
|
||||
if _, err := gd.rh.JSONSet(gd.strid(), "$._members."+gd.tid(mid), memdoc, SetOptionXX); err != nil {
|
||||
if _, err := gd.rh.JSONMerge(gd.strid(), prefix+"._body", doc, gocommon.RedisonSetOptionXX); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return memdoc, nil
|
||||
if err := gd.rh.JSONMDel(gd.strid(), []string{prefix + "._invite", prefix + "._invite_exp"}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gd.loadMemberFull(tid)
|
||||
}
|
||||
|
||||
func (gd *groupDoc) removeMember(mid accountID) error {
|
||||
@ -179,21 +189,41 @@ func (gd *groupDoc) removeMember(mid accountID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (gd *groupDoc) getMembers() (map[string]any, error) {
|
||||
res, err := gd.rh.JSONGet(gd.strid(), "$._members")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var temp []map[string]any
|
||||
err = json.Unmarshal([]byte(res.(string)), &temp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]any)
|
||||
for k, v := range temp[0] {
|
||||
body := v.(map[string]any)["_body"]
|
||||
out[gd.mid(k).Hex()] = body
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type groupInMemory struct {
|
||||
*groupConfig
|
||||
sendUpstreamMessage func(*wshandler.UpstreamMessage)
|
||||
sendEnterRoomMessage func(groupID, accountID)
|
||||
sendLeaveRoomMessage func(groupID, accountID)
|
||||
rh *RedisonHandler
|
||||
rh *gocommon.RedisonHandler
|
||||
}
|
||||
|
||||
func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc bson.M) (*groupDoc, error) {
|
||||
tid := makeTid(newid, charge)
|
||||
|
||||
gd := &groupDoc{
|
||||
Body: bson.M{},
|
||||
Members: map[string]*memberDoc{
|
||||
tid: {
|
||||
Members: map[string]any{
|
||||
tid: &memberDoc{
|
||||
Body: chargeDoc,
|
||||
Invite: false,
|
||||
InviteExpire: 0,
|
||||
@ -205,7 +235,7 @@ func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc
|
||||
id: newid,
|
||||
}
|
||||
|
||||
_, err := gm.rh.JSONSet(gd.strid(), "$", gd, SetOptionNX)
|
||||
_, err := gm.rh.JSONSet(gd.strid(), "$", gd, gocommon.RedisonSetOptionNX)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -243,18 +273,36 @@ func (gm *groupInMemory) Candidate(gid groupID, mid accountID, doc bson.M) error
|
||||
var errGroupNotExist = errors.New("group does not exist")
|
||||
|
||||
func (gm *groupInMemory) Join(gid groupID, mid accountID, doc bson.M) error {
|
||||
group, err := gm.find(gid)
|
||||
gd, err := gm.find(gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if group == nil {
|
||||
if gd == nil {
|
||||
// 그룹이 없다. 실패
|
||||
return errGroupNotExist
|
||||
}
|
||||
|
||||
// 내 정보 업데이트할 때에도 사용됨
|
||||
_, err = group.addMember(mid, doc)
|
||||
if memdoc, err := gd.addMember(mid, doc); err == nil {
|
||||
// 기존 유저에게 새 유저 알림
|
||||
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||
Target: "#" + gid.Hex(),
|
||||
Body: map[string]any{
|
||||
gd.tid(mid): memdoc,
|
||||
},
|
||||
Tag: []string{"MemberDocFull"},
|
||||
})
|
||||
|
||||
gm.sendEnterRoomMessage(gid, mid)
|
||||
|
||||
// 새 멤버에 그룹 전체를 알림
|
||||
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||
Target: "@" + mid.Hex(),
|
||||
Body: gd.loadFull(),
|
||||
Tag: []string{"GroupDocFull"},
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@ -269,7 +317,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
|
||||
}
|
||||
|
||||
// targetid에 초대한 mid가 들어있다.
|
||||
already, err := gm.rh.Get(gm.rh.ctx, targetid.Hex()).Result()
|
||||
already, err := gm.rh.Get(context.Background(), targetid.Hex()).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return "", err
|
||||
}
|
||||
@ -313,7 +361,7 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
|
||||
}
|
||||
|
||||
// 초대 중 표시
|
||||
_, err = gm.rh.SetNX(gm.rh.ctx, targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result()
|
||||
_, err = gm.rh.SetNX(context.Background(), targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -340,9 +388,7 @@ func (gm *groupInMemory) CancelInvitation(gid groupID, tid ticketID) error {
|
||||
var errInvitationExpired = errors.New("invitation is already expired")
|
||||
|
||||
func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error {
|
||||
logger.Println("accept invitation key :", mid.Hex())
|
||||
|
||||
cnt, err := gm.rh.Del(gm.rh.ctx, mid.Hex()).Result()
|
||||
cnt, err := gm.rh.Del(context.Background(), mid.Hex()).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -351,7 +397,7 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
|
||||
return errInvitationExpired
|
||||
}
|
||||
|
||||
gd := groupDoc{
|
||||
gd := &groupDoc{
|
||||
id: gid,
|
||||
rh: gm.rh,
|
||||
}
|
||||
@ -366,25 +412,13 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
|
||||
},
|
||||
Tag: []string{"MemberDocFull"},
|
||||
})
|
||||
|
||||
gm.sendEnterRoomMessage(gid, mid)
|
||||
|
||||
// 새 멤버에 그룹 전체를 알림
|
||||
full, err := gm.rh.JSONGet(gd.strid(), "$")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var temp []*groupDoc
|
||||
err = json.Unmarshal([]byte(full.(string)), &temp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
test, _ := json.Marshal(temp[0])
|
||||
logger.Println(string(test))
|
||||
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||
Target: "@" + mid.Hex(),
|
||||
Body: temp[0],
|
||||
Body: gd.loadFull(),
|
||||
Tag: []string{"GroupDocFull"},
|
||||
})
|
||||
}
|
||||
@ -393,8 +427,16 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
|
||||
return err
|
||||
}
|
||||
|
||||
func (gm *groupInMemory) QueryGroupMembers(gid groupID) (bson.M, error) {
|
||||
gd := groupDoc{
|
||||
id: gid,
|
||||
rh: gm.rh,
|
||||
}
|
||||
return gd.getMembers()
|
||||
}
|
||||
|
||||
func (gm *groupInMemory) DenyInvitation(gid groupID, mid accountID, tid ticketID) error {
|
||||
gm.rh.Del(gm.rh.ctx, mid.Hex()).Result()
|
||||
gm.rh.Del(context.Background(), mid.Hex()).Result()
|
||||
gd := groupDoc{
|
||||
id: gid,
|
||||
rh: gm.rh,
|
||||
@ -435,10 +477,11 @@ func (gm *groupInMemory) Leave(gid groupID, mid accountID) error {
|
||||
}
|
||||
|
||||
func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bson.M) error {
|
||||
gd := groupDoc{
|
||||
gd := &groupDoc{
|
||||
id: gid,
|
||||
rh: gm.rh,
|
||||
}
|
||||
|
||||
prefixPath := fmt.Sprintf("$._members.%s.", gd.tid(mid))
|
||||
err := gm.rh.JSONMSetRel(gd.strid(), prefixPath, doc)
|
||||
if err != nil {
|
||||
@ -494,7 +537,7 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, su
|
||||
// 각 함수에서는 publish
|
||||
gm := &groupInMemory{
|
||||
groupConfig: cfg,
|
||||
rh: NewRedisonHandler(ctx, redisClient),
|
||||
rh: gocommon.NewRedisonHandler(ctx, redisClient),
|
||||
sendUpstreamMessage: func(msg *wshandler.UpstreamMessage) {
|
||||
wsh.SendUpstreamMessage(region, msg)
|
||||
},
|
||||
|
||||
@ -1,372 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
type SetOption = string
|
||||
type GetOption = [2]any
|
||||
|
||||
const (
|
||||
// JSONSET command Options
|
||||
SetOptionNX SetOption = "NX"
|
||||
SetOptionXX SetOption = "XX"
|
||||
)
|
||||
|
||||
var (
|
||||
GetOptionSPACE = GetOption{"SPACE", " "}
|
||||
GetOptionINDENT = GetOption{"INDENT", "\t"}
|
||||
GetOptionNEWLINE = GetOption{"NEWLINE", "\n"}
|
||||
GetOptionNOESCAPE = GetOption{"NOESCAPE", ""}
|
||||
)
|
||||
|
||||
// gocommon으로 옮길 거
|
||||
type RedisonHandler struct {
|
||||
*redis.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewRedisonHandler(ctx context.Context, redisClient *redis.Client) *RedisonHandler {
|
||||
return &RedisonHandler{
|
||||
Client: redisClient,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func respToArray[T any](resp any, err error) ([]T, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resArr := resp.([]any)
|
||||
v := make([]T, len(resArr))
|
||||
for i, e := range resArr {
|
||||
v[i] = e.(T)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func appendArgs[T any](args []any, ext ...T) []any {
|
||||
for _, e := range ext {
|
||||
args = append(args, e)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONMSetRel(key string, prefixPath string, kv map[string]any) error {
|
||||
if len(prefixPath) > 0 && !strings.HasSuffix(prefixPath, ".") {
|
||||
prefixPath += "."
|
||||
}
|
||||
|
||||
pl := rh.Pipeline()
|
||||
for path, obj := range kv {
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pl.Do(rh.ctx, "JSON.SET", key, prefixPath+path, b)
|
||||
}
|
||||
|
||||
cmders, err := pl.Exec(rh.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cmder := range cmders {
|
||||
if cmder.Err() != nil {
|
||||
return cmder.Err()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error {
|
||||
return rh.JSONMSetRel(key, "", kv)
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONSet(key, path string, obj any, opts ...SetOption) (bool, error) {
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
args := []any{
|
||||
"JSON.SET",
|
||||
key,
|
||||
path,
|
||||
b,
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
args = append(args, opts[0])
|
||||
}
|
||||
|
||||
res, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return res.(string) == "OK", nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONGet(key, path string, opts ...GetOption) (res any, err error) {
|
||||
args := appendArgs[string]([]any{
|
||||
"JSON.GET",
|
||||
key,
|
||||
}, strings.Split(path, " ")...)
|
||||
|
||||
for _, opt := range opts {
|
||||
args = append(args, opt[:]...)
|
||||
}
|
||||
|
||||
return rh.Do(rh.ctx, args...).Result()
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONGetString(key, path string) ([]string, error) {
|
||||
return respToArray[string](rh.JSONResp(key, path))
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONGetInt64(key, path string) ([]int64, error) {
|
||||
return respToArray[int64](rh.JSONResp(key, path))
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONMGet(path string, keys ...string) (res any, err error) {
|
||||
args := appendArgs[string]([]any{
|
||||
"JSON.MGET",
|
||||
path,
|
||||
}, keys...)
|
||||
return rh.Do(rh.ctx, args...).Result()
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONMDel(key string, paths []string) error {
|
||||
pl := rh.Pipeline()
|
||||
for _, path := range paths {
|
||||
args := []any{
|
||||
"JSON.DEL",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
pl.Do(rh.ctx, args...)
|
||||
}
|
||||
_, err := pl.Exec(rh.ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONDel(key, path string) (int64, error) {
|
||||
args := []any{
|
||||
"JSON.DEL",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
resp, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.(int64), nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONType(key, path string) ([]string, error) {
|
||||
args := []any{
|
||||
"JSON.TYPE",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
return respToArray[string](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONNumIncrBy(key, path string, number int) ([]any, error) {
|
||||
args := []any{
|
||||
"JSON.NUMINCRBY",
|
||||
key,
|
||||
path,
|
||||
number,
|
||||
}
|
||||
resp, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.([]any), nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONNumMultBy(key, path string, number int) (res any, err error) {
|
||||
args := []any{
|
||||
"JSON.NUMMULTBY",
|
||||
key,
|
||||
path,
|
||||
number,
|
||||
}
|
||||
resp, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.([]any), nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONStrAppend(key, path string, jsonstring string) ([]int64, error) {
|
||||
args := []any{
|
||||
"JSON.STRAPPEND",
|
||||
key,
|
||||
path,
|
||||
fmt.Sprintf(`'"%s"'`, jsonstring),
|
||||
}
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONStrLen(key, path string) (res []int64, err error) {
|
||||
args := []any{
|
||||
"JSON.STRLEN",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrAppend(key, path string, values ...any) (int64, error) {
|
||||
args := []any{
|
||||
"JSON.ARRAPPEND",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
resp, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.(int64), nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrLen(key, path string) ([]int64, error) {
|
||||
args := []any{
|
||||
"JSON.ARRLEN",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrPop(key, path string, index int) (res any, err error) {
|
||||
args := []any{
|
||||
"JSON.ARRPOP",
|
||||
key,
|
||||
path,
|
||||
index,
|
||||
}
|
||||
resp, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.([]any)[0], nil
|
||||
}
|
||||
|
||||
func appendValues(args []any, values ...any) []any {
|
||||
for _, jsonValue := range values {
|
||||
switch jsonValue := jsonValue.(type) {
|
||||
case string:
|
||||
args = append(args, fmt.Sprintf(`'"%s"'`, jsonValue))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
args = append(args, jsonValue)
|
||||
default:
|
||||
bt, _ := json.Marshal(jsonValue)
|
||||
args = append(args, bt)
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrIndex(key, path string, jsonValue any, optionalRange ...int) ([]int64, error) {
|
||||
args := appendValues([]any{
|
||||
"JSON.ARRINDEX",
|
||||
key,
|
||||
path,
|
||||
}, jsonValue)
|
||||
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrTrim(key, path string, start, end int) (res any, err error) {
|
||||
args := []any{
|
||||
"JSON.ARRTRIM",
|
||||
key,
|
||||
path,
|
||||
start,
|
||||
end,
|
||||
}
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONArrInsert(key, path string, index int, values ...any) (res any, err error) {
|
||||
args := appendValues([]any{
|
||||
"JSON.ARRINSERT",
|
||||
key,
|
||||
path,
|
||||
index,
|
||||
}, values...)
|
||||
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONObjKeys(key, path string) ([]string, error) {
|
||||
args := []any{
|
||||
"JSON.OBJKEYS",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
|
||||
res, err := rh.Do(rh.ctx, args...).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resArr := res.([]any)
|
||||
resArr = resArr[0].([]any)
|
||||
slc := make([]string, len(resArr))
|
||||
|
||||
for i, r := range resArr {
|
||||
slc[i] = r.(string)
|
||||
}
|
||||
|
||||
return slc, nil
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONObjLen(key, path string) ([]int64, error) {
|
||||
args := []any{
|
||||
"JSON.OBJLEN",
|
||||
key,
|
||||
}
|
||||
|
||||
if path != "$" {
|
||||
args = append(args, path)
|
||||
}
|
||||
|
||||
return respToArray[int64](rh.Do(rh.ctx, args...).Result())
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONDebug(key, path string) (res any, err error) {
|
||||
args := []any{
|
||||
"JSON.DEBUG",
|
||||
"MEMORY",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
return rh.Do(rh.ctx, args...).Result()
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONForget(key, path string) (int64, error) {
|
||||
return rh.JSONDel(key, path)
|
||||
}
|
||||
|
||||
func (rh *RedisonHandler) JSONResp(key, path string) (res any, err error) {
|
||||
args := []any{
|
||||
"JSON.RESP",
|
||||
key,
|
||||
path,
|
||||
}
|
||||
return rh.Do(rh.ctx, args...).Result()
|
||||
}
|
||||
@ -9,7 +9,9 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
"repositories.action2quare.com/ayo/gocommon/wshandler"
|
||||
@ -84,6 +86,7 @@ type Tavern struct {
|
||||
|
||||
type subTavern struct {
|
||||
mongoClient gocommon.MongoClient
|
||||
redisClient *redis.Client
|
||||
wsh *wshandler.WebsocketHandler
|
||||
region string
|
||||
groups map[string]group
|
||||
@ -141,7 +144,7 @@ func (tv *Tavern) Cleanup() {
|
||||
}
|
||||
|
||||
func (tv *Tavern) prepare(ctx context.Context) error {
|
||||
for region := range config.RegionStorage {
|
||||
for region, addr := range config.RegionStorage {
|
||||
var dbconn gocommon.MongoClient
|
||||
var err error
|
||||
var groupinstance group
|
||||
@ -154,9 +157,15 @@ func (tv *Tavern) prepare(ctx context.Context) error {
|
||||
methods[method.Name] = method
|
||||
}
|
||||
|
||||
redisClient, err := gocommon.NewRedisClient(addr.Redis["tavern"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sub := &subTavern{
|
||||
wsh: tv.wsh,
|
||||
mongoClient: dbconn,
|
||||
redisClient: redisClient,
|
||||
region: region,
|
||||
methods: methods,
|
||||
}
|
||||
@ -191,7 +200,7 @@ func (tv *Tavern) prepare(ctx context.Context) error {
|
||||
|
||||
func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
|
||||
for _, sub := range tv.subTaverns {
|
||||
tv.wsh.RegisterReceiver(sub.region, sub.clientMessageReceived)
|
||||
tv.wsh.RegisterReceiver(sub.region, sub)
|
||||
var pattern string
|
||||
if sub.region == "default" {
|
||||
pattern = gocommon.MakeHttpHandlerPattern(prefix, "api")
|
||||
@ -204,11 +213,11 @@ func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageType wshandler.WebSocketMessageType, body io.Reader) {
|
||||
func (sub *subTavern) OnClientMessageReceived(sender *wshandler.Sender, messageType wshandler.WebSocketMessageType, body io.Reader) {
|
||||
if messageType == wshandler.Connected {
|
||||
|
||||
logger.Println("OnClientMessageReceived : connected ", sender.Accid.Hex())
|
||||
} else if messageType == wshandler.Disconnected {
|
||||
|
||||
logger.Println("OnClientMessageReceived : disconnected ", sender.Accid.Hex())
|
||||
} else if messageType == wshandler.BinaryMessage {
|
||||
var msg map[string][]any
|
||||
dec := json.NewDecoder(body)
|
||||
@ -242,6 +251,20 @@ func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageTyp
|
||||
}
|
||||
}
|
||||
|
||||
func (sub *subTavern) OnRoomCreated(region, name string) {
|
||||
_, err := sub.redisClient.Persist(context.Background(), name).Result()
|
||||
if err != nil {
|
||||
logger.Println("OnRoomCreate Persist failed :", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sub *subTavern) OnRoomDestroyed(region, name string) {
|
||||
_, err := sub.redisClient.Expire(context.Background(), name, 3600*time.Second).Result()
|
||||
if err != nil {
|
||||
logger.Println("OnRoomDestroyed Persist failed :", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sub *subTavern) api(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
|
||||
@ -3,16 +3,37 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
)
|
||||
|
||||
func TestPubSub(t *testing.T) {
|
||||
opt0, _ := redis.ParseURL("redis://192.168.8.94:6380/0")
|
||||
opt1, _ := redis.ParseURL("redis://192.168.8.94:6380/1")
|
||||
|
||||
rc0 := redis.NewClient(opt0)
|
||||
rc1 := redis.NewClient(opt1)
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
rc1.Publish(context.Background(), "__testchan", "real???")
|
||||
fmt.Println("published")
|
||||
}()
|
||||
|
||||
pubsub := rc0.Subscribe(context.Background(), "__testchan")
|
||||
msg, err := pubsub.ReceiveMessage(context.Background())
|
||||
fmt.Println(msg.Payload, err)
|
||||
}
|
||||
|
||||
func TestReJSON(t *testing.T) {
|
||||
rc := redis.NewClient(&redis.Options{Addr: "192.168.8.94:6380"})
|
||||
rh := NewRedisonHandler(context.Background(), rc)
|
||||
rh := gocommon.NewRedisonHandler(context.Background(), rc)
|
||||
|
||||
testDoc := map[string]any{
|
||||
"members": map[string]any{
|
||||
|
||||
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.20
|
||||
require (
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
go.mongodb.org/mongo-driver v1.11.7
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
20
go.sum
20
go.sum
@ -112,3 +112,23 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732 h1
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e h1:/eG6tAQzEaN178Aib+/erjHrE/+IjIVLRSmP4gx6D7E=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718004527-4b35e0e6386b h1:K3YQXnVP/W6LzwGzqOxwKmFUD5IrrNPEWYcN/fSinck=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718004527-4b35e0e6386b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718005518-289af24a8ffa h1:YmzJ1YccK3BxC/NbfB11SEUG1S6Lkz6ejg4kS3q/3Qc=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718005518-289af24a8ffa/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020415-82abcddb497b h1:baO9csa0Esnp7UW+L8zJW/ygpjGHRve4rU2/1pVvXQg=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020415-82abcddb497b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020838-c21017d2cd8b h1:FqLKDrFji0+giFwAJ3oV6dIOR6Sd/aaay76WgWIEVR8=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718020838-c21017d2cd8b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718032106-40a603522d40 h1:VyFfS0d6pTX2HbZoDHOxJwag4aVSLOh/LrQXqfSJLBg=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718032106-40a603522d40/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718083804-d724cc84fa94 h1:iQPrRcZ6XfFblpVHxe/CIoWyTj7imF+3edIGSX6ZMM8=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718083804-d724cc84fa94/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718084512-89fa9e4ac585 h1:Wy6qjZ0uHfp02/H688zotRfzYGRPjun7Qay0Z9B/hSg=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718084512-89fa9e4ac585/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718105124-72a683fed2c0 h1:8LmRo2nKaLi4QCmO/agSpNTmCD0EdwFycjHjOweQJp8=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230718105124-72a683fed2c0/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003101-256bfd030c29 h1:ADScrqJgmk/TfyOu/6oXD3WkSH8sh3Bw360O8GKuEV8=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003101-256bfd030c29/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d h1:eMzrvVkQfbs5X5dcw80TGGKtJ+6XELl7zNsWiuq4gzs=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230719003337-29b2f258507d/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||
|
||||
Reference in New Issue
Block a user