diff --git a/common/forum_perms_store.go b/common/forum_perms_store.go index 8ef1fe64..f27de96a 100644 --- a/common/forum_perms_store.go +++ b/common/forum_perms_store.go @@ -155,6 +155,7 @@ func (fps *MemoryForumPermsStore) Reload(fid int) error { } DebugDetailf("group.CanSee (length %d): %+v \n", len(group.CanSee), group.CanSee) } + TopicListThaw.Thaw() return nil } diff --git a/common/forum_store.go b/common/forum_store.go index 8804a145..fd87b4a7 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -121,6 +121,7 @@ func (mfs *MemoryForumStore) LoadForums() error { addForum(forum) } mfs.forumView.Store(forumView) + TopicListThaw.Thaw() return rows.Err() } @@ -185,6 +186,7 @@ func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) { forum.Link = BuildForumURL(NameToSlug(forum.Name), forum.ID) forum.LastTopic = Topics.DirtyGet(forum.LastTopicID) forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) + TopicListThaw.Thaw() return forum, err } @@ -213,6 +215,7 @@ func (mfs *MemoryForumStore) Reload(id int) error { forum.LastReplyer = Users.DirtyGet(forum.LastReplyerID) mfs.CacheSet(forum) + TopicListThaw.Thaw() return nil } @@ -283,6 +286,7 @@ func (mfs *MemoryForumStore) Delete(id int) error { } _, err := mfs.delete.Exec(id) mfs.CacheDelete(id) + TopicListThaw.Thaw() return err } diff --git a/common/group.go b/common/group.go index 110193b2..80767259 100644 --- a/common/group.go +++ b/common/group.go @@ -58,7 +58,6 @@ func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err err if err != nil { return err } - Groups.Reload(group.ID) return nil } @@ -68,7 +67,6 @@ func (group *Group) Update(name string, tag string) (err error) { if err != nil { return err } - Groups.Reload(group.ID) return nil } @@ -83,7 +81,7 @@ func (group *Group) UpdatePerms(perms map[string]bool) (err error) { if err != nil { return err } - return RebuildGroupPermissions(group.ID) + return Groups.Reload(group.ID) } // Copy gives you a non-pointer concurrency safe copy of the group diff --git a/common/group_store.go b/common/group_store.go index 582599ba..edcadff0 100644 --- a/common/group_store.go +++ b/common/group_store.go @@ -90,6 +90,7 @@ func (mgs *MemoryGroupStore) LoadGroups() error { DebugLog("Binding the Not Loggedin Group") GuestPerms = mgs.dirtyGetUnsafe(6).Perms + TopicListThaw.Thaw() return nil } @@ -152,6 +153,7 @@ func (mgs *MemoryGroupStore) Reload(id int) error { if err != nil { LogError(err) } + TopicListThaw.Thaw() return nil } @@ -280,6 +282,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod mgs.groupCount++ mgs.Unlock() + TopicListThaw.Thaw() return gid, FPStore.ReloadAll() //return gid, TopicList.RebuildPermTree() } diff --git a/common/templates/context.go b/common/templates/context.go index 08202f86..68ab718e 100644 --- a/common/templates/context.go +++ b/common/templates/context.go @@ -1,14 +1,22 @@ package tmpl import ( - "errors" "reflect" ) +type Fragment struct { + Body string + TemplateName string + Index int + Seen bool +} + type OutBufferFrame struct { Body string Type string TemplateName string + Extra interface{} + Extra2 interface{} } type CContext struct { @@ -18,39 +26,12 @@ type CContext struct { OutBuf *[]OutBufferFrame } -func (con *CContext) Push(nType string, body string) { - *con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, nType, con.TemplateName}) +func (con *CContext) Push(nType string, body string) (index int) { + *con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, nType, con.TemplateName, nil, nil}) + return len(*con.OutBuf) - 1 } -func (con *CContext) GetLastType() string { - outBuf := *con.OutBuf - if len(outBuf) == 0 { - return "" - } - return outBuf[len(outBuf)-1].Type -} - -func (con *CContext) GetLastBody() string { - outBuf := *con.OutBuf - if len(outBuf) == 0 { - return "" - } - return outBuf[len(outBuf)-1].Body -} - -func (con *CContext) SetLastBody(newBody string) error { - outBuf := *con.OutBuf - if len(outBuf) == 0 { - return errors.New("outbuf is empty") - } - outBuf[len(outBuf)-1].Body = newBody - return nil -} - -func (con *CContext) GetLastTemplate() string { - outBuf := *con.OutBuf - if len(outBuf) == 0 { - return "" - } - return outBuf[len(outBuf)-1].TemplateName +func (con *CContext) PushText(body string, fragIndex int, fragOutIndex int) (index int) { + *con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "text", con.TemplateName, fragIndex, fragOutIndex}) + return len(*con.OutBuf) - 1 } diff --git a/common/templates/templates.go b/common/templates/templates.go index ddf5d3b9..8fa71476 100644 --- a/common/templates/templates.go +++ b/common/templates/templates.go @@ -40,12 +40,6 @@ type CTemplateConfig struct { PackageName string } -type Fragment struct { - Body string - TemplateName string - Index int -} - // nolint type CTemplateSet struct { templateList map[string]*parse.Tree @@ -53,7 +47,7 @@ type CTemplateSet struct { funcMap map[string]interface{} importMap map[string]string TemplateFragmentCount map[string]int - Fragments map[string]int + FragOnce map[string]bool fragmentCursor map[string]int FragOut string fragBuf []Fragment @@ -122,6 +116,16 @@ func (c *CTemplateSet) SetBuildTags(tags string) { c.buildTags = tags } +type SkipBlock struct { + Frags map[int]int + LastCount int + ClosestFragSkip int +} +type Skipper struct { + Count int + Index int +} + func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) { if c.config.Debug { fmt.Println("Compiling template '" + name + "'") @@ -173,10 +177,11 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe c.localVars = make(map[string]map[string]VarItemReflect) c.localVars[fname] = make(map[string]VarItemReflect) c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect} - if c.Fragments == nil { - c.Fragments = make(map[string]int) + if c.FragOnce == nil { + c.FragOnce = make(map[string]bool) } c.fragmentCursor = map[string]int{fname: 0} + c.fragBuf = nil c.langIndexToName = nil // TODO: Is this the first template loaded in? We really should have some sort of constructor for CTemplateSet @@ -187,6 +192,11 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe c.rootIterate(c.templateList[fname], con) c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1 + _, ok := c.FragOnce[fname] + if !ok { + c.FragOnce[fname] = true + } + if len(c.langIndexToName) > 0 { c.importMap[langPkg] = langPkg } @@ -195,7 +205,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe for _, item := range c.importMap { importList += "import \"" + item + "\"\n" } - var varString string for _, varItem := range c.varList { varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" @@ -205,7 +214,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe if c.buildTags != "" { fout += "// +build " + c.buildTags + "\n\n" } - fout += "// Code generated by Gosora. More below:\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n" fout += "package " + c.config.PackageName + "\n" + importList + "\n" @@ -238,20 +246,74 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe fout += "var plist = phrases.GetTmplPhrasesBytes(" + fname + "_tmpl_phrase_id)\n" } fout += varString - for _, frame := range outBuf { - fout += frame.Body + + var skipped = make(map[string]*SkipBlock) // map[templateName]*SkipBlock{map[atIndexAndAfter]skipThisMuch,lastCount} + + var writeTextFrame = func(tmplName string, index int) { + out := "w.Write(" + tmplName + "_frags[" + strconv.Itoa(index) + "]" + ")\n" + c.detail("writing ", out) + fout += out + } + + for fid := 0; len(outBuf) > fid; fid++ { + frame := outBuf[fid] + if frame.Type == "text" { + c.detail("text frame:") + c.detail(frame) + oid := fid + skipBlock, ok := skipped[frame.TemplateName] + if !ok { + skipBlock = &SkipBlock{make(map[int]int), 0, 0} + skipped[frame.TemplateName] = skipBlock + } + skip := skipBlock.LastCount + c.detailf("skipblock %+v\n", skipBlock) + for len(outBuf) > fid+1 && outBuf[fid+1].Type == "text" && outBuf[fid+1].TemplateName == frame.TemplateName { + next := outBuf[fid+1] + c.detail("next frame:", next) + c.detail("frame frag:", c.fragBuf[frame.Extra2.(int)]) + c.detail("next frag:", c.fragBuf[next.Extra2.(int)]) + c.fragBuf[frame.Extra2.(int)].Body += c.fragBuf[next.Extra2.(int)].Body + c.fragBuf[next.Extra2.(int)].Seen = true + fid++ + skipBlock.LastCount += (fid - oid) + skipBlock.Frags[frame.Extra.(int)] = skipBlock.LastCount + } + writeTextFrame(frame.TemplateName, frame.Extra.(int)-skip) + } else { + c.detail(frame.Type + " frame") + fout += frame.Body + } } fout += "return nil\n}\n" + var writeFrag = func(tmplName string, index int, body string) { + fragmentPrefix := tmplName + "_frags[" + strconv.Itoa(index) + "]" + " = []byte(`" + body + "`)\n" + c.detail("writing ", fragmentPrefix) + c.FragOut += fragmentPrefix + } + + for _, frag := range c.fragBuf { + c.detail("frag: ", frag) + if frag.Seen { + c.detail("invisible") + continue + } + skipBlock := skipped[frag.TemplateName] + skip := skipBlock.Frags[skipBlock.ClosestFragSkip] + _, ok := skipBlock.Frags[frag.Index] + if ok { + skipBlock.ClosestFragSkip = frag.Index + } + c.detailf("skipblock %+v\n", skipBlock) + c.detail("skipping ", skip) + writeFrag(frag.TemplateName, frag.Index-skip, frag.Body) + } + fout = strings.Replace(fout, `)) w.Write([]byte(`, " + ", -1) fout = strings.Replace(fout, "` + `", "", -1) - for _, frag := range c.fragBuf { - fragmentPrefix := frag.TemplateName + "_frags[" + strconv.Itoa(frag.Index) + "]" - c.FragOut += fragmentPrefix + " = []byte(`" + frag.Body + "`)\n" - } - if c.config.Debug { for index, count := range c.stats { fmt.Println(index+": ", strconv.Itoa(count)) @@ -264,6 +326,7 @@ w.Write([]byte(`, " + ", -1) } func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) { + c.dumpCall("rootIterate", tree, con) c.detail(tree.Root) treeLength := len(tree.Root.Nodes) for index, node := range tree.Root.Nodes { @@ -275,10 +338,12 @@ func (c *CTemplateSet) rootIterate(tree *parse.Tree, con CContext) { } c.compileSwitch(con, node) } + c.retCall("rootIterate") } func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) { c.dumpCall("compileSwitch", con, node) + defer c.retCall("compileSwitch") switch node := node.(type) { case *parse.ActionNode: c.detail("Action Node") @@ -333,15 +398,12 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) { return } - fragmentName := con.TemplateName + "_" + strconv.Itoa(c.fragmentCursor[con.TemplateName]) - fragmentPrefix := con.TemplateName + "_frags[" + strconv.Itoa(c.fragmentCursor[con.TemplateName]) + "]" - _, ok := c.Fragments[fragmentName] - if !ok { - c.Fragments[fragmentName] = len(node.Text) - c.fragBuf = append(c.fragBuf, Fragment{string(node.Text), con.TemplateName, c.fragmentCursor[con.TemplateName]}) - } - c.fragmentCursor[con.TemplateName] = c.fragmentCursor[con.TemplateName] + 1 - con.Push("text", "w.Write("+fragmentPrefix+")\n") + nodeText := string(node.Text) + fragIndex := c.fragmentCursor[con.TemplateName] + _, ok := c.FragOnce[con.TemplateName] + c.fragBuf = append(c.fragBuf, Fragment{nodeText, con.TemplateName, fragIndex, ok}) + con.PushText(strconv.Itoa(fragIndex), fragIndex, len(c.fragBuf)-1) + c.fragmentCursor[con.TemplateName] = fragIndex + 1 default: c.unknownNode(node) } @@ -349,6 +411,7 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) { func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) { c.dumpCall("compileRangeNode", con, node) + defer c.retCall("compileRangeNode") c.detail("node.Pipe: ", node.Pipe) var expr string var outVal reflect.Value @@ -653,8 +716,11 @@ func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse.CommandNod func (c *CTemplateSet) compileIdentSwitch(con CContext, node *parse.CommandNode) (out string, val reflect.Value, literal bool) { c.dumpCall("compileIdentSwitch", con, node) - var litString = func(inner string) { - out = "w.Write([]byte(" + inner + "))\n" + var litString = func(inner string, bytes bool) { + if !bytes { + inner = "[]byte(" + inner + ")" + } + out = "w.Write(" + inner + ")\n" literal = true } ArgLoop: @@ -682,7 +748,7 @@ ArgLoop: leftParam, _ := c.compileIfVarSub(con, leftOperand) // TODO: Refactor this // TODO: Validate that this is actually a time.Time - litString("time.Since(" + leftParam + ").String()") + litString("time.Since("+leftParam+").String()", false) c.importMap["time"] = "time" break ArgLoop case "dock": @@ -692,7 +758,6 @@ ArgLoop: if len(leftOperand) == 0 || len(rightOperand) == 0 { panic("The left or right operand for function dock cannot be left blank") } - leftParam := leftOperand if leftOperand[0] != '"' { leftParam, _ = c.compileIfVarSub(con, leftParam) @@ -707,7 +772,7 @@ ArgLoop: val = val3 // TODO: Refactor this - litString("common.BuildWidget(" + leftParam + "," + rightParam + ")") + litString("common.BuildWidget("+leftParam+","+rightParam+")", false) break ArgLoop case "lang": // TODO: Implement string literals properly @@ -718,11 +783,10 @@ ArgLoop: if leftOperand[0] != '"' { panic("Phrase names cannot be dynamic") } - // ! Slightly crude but it does the job leftParam := strings.Replace(leftOperand, "\"", "", -1) c.langIndexToName = append(c.langIndexToName, leftParam) - litString("plist[" + strconv.Itoa(len(c.langIndexToName)-1) + "]") + litString("plist["+strconv.Itoa(len(c.langIndexToName)-1)+"]", true) break ArgLoop case "level": // TODO: Implement level literals @@ -732,7 +796,7 @@ ArgLoop: } leftParam, _ := c.compileIfVarSub(con, leftOperand) // TODO: Refactor this - litString("phrases.GetLevelPhrase(" + leftParam + ")") + litString("phrases.GetLevelPhrase("+leftParam+")", false) c.importMap[langPkg] = langPkg break ArgLoop case "scope": @@ -991,6 +1055,7 @@ func (c *CTemplateSet) retCall(name string, params ...interface{}) { func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.Value, assLines string, onEnd func(string) string) { c.dumpCall("compileVarSub", con, varname, val, assLines, onEnd) + defer c.retCall("compileVarSub") if onEnd == nil { onEnd = func(in string) string { return in @@ -999,7 +1064,7 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V // Is this a literal string? if len(varname) != 0 && varname[0] == '"' { - con.Push("varsub", onEnd(assLines+"w.Write([]byte("+varname+"))\n")) + con.Push("lvarsub", onEnd(assLines+"w.Write([]byte("+varname+"))\n")) return } for _, varItem := range c.varList { @@ -1030,17 +1095,23 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V switch val.Kind() { case reflect.Int: c.importMap["strconv"] = "strconv" - base = "w.Write([]byte(strconv.Itoa(" + varname + ")))\n" + base = "[]byte(strconv.Itoa(" + varname + "))" case reflect.Bool: - base = "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n" + con.Push("startif", "if "+varname+" {\n") + con.Push("varsub", "w.Write([]byte(\"true\"))") + con.Push("endif", "} ") + con.Push("startelse", "else {\n") + con.Push("varsub", "w.Write([]byte(\"false\"))") + con.Push("endelse", "}\n") + return case reflect.String: if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") { varname = "string(" + varname + ")" } - base = "w.Write([]byte(" + varname + "))\n" + base = "[]byte(" + varname + ")" case reflect.Int64: c.importMap["strconv"] = "strconv" - base = "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))\n" + base = "[]byte(strconv.FormatInt(" + varname + ", 10))" default: if !val.IsValid() { panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?") @@ -1050,12 +1121,17 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V fmt.Println("Unknown Type:", val.Type().Name()) panic("-- I don't know what this variable's type is o.o\n") } + base = "w.Write(" + base + ")\n" c.detail("base: ", base) - con.Push("varsub", onEnd(assLines+base)) + if assLines == "" { + con.Push("varsub", base) + } else { + con.Push("lvarsub", onEnd(assLines+base)) + } } func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNode) { - c.debugCall("compileSubTemplate", pcon, node) + c.dumpCall("compileSubTemplate", pcon, node) c.detail("Template Node: ", node.Name) fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name)) @@ -1107,26 +1183,33 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod c.localVars[fname] = make(map[string]VarItemReflect) c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect} c.fragmentCursor[fname] = 0 + con.Push("starttemplate", "{\n") c.rootIterate(subtree, con) + con.Push("endtemplate", "}\n") c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1 + + _, ok := c.FragOnce[fname] + if !ok { + c.FragOnce[fname] = true + } } // TODO: Should we rethink the way the log methods work or their names? func (c *CTemplateSet) detail(args ...interface{}) { if c.config.SuperDebug { - fmt.Println(args...) + log.Println(args...) } } func (c *CTemplateSet) detailf(left string, args ...interface{}) { if c.config.SuperDebug { - fmt.Printf(left, args...) + log.Printf(left, args...) } } func (c *CTemplateSet) error(args ...interface{}) { if c.config.Debug { - fmt.Println(args...) + log.Println(args...) } } diff --git a/common/thaw.go b/common/thaw.go new file mode 100644 index 00000000..23c05b5b --- /dev/null +++ b/common/thaw.go @@ -0,0 +1,70 @@ +package common + +import ( + "sync" + "sync/atomic" +) + +var TopicListThaw ThawInt + +type ThawInt interface { + Thawed() bool + Thaw() + + Tick() error +} + +type SingleServerThaw struct { + DefaultThaw +} + +func NewSingleServerThaw() *SingleServerThaw { + thaw := &SingleServerThaw{} + if Config.ServerCount == 1 { + AddScheduledSecondTask(thaw.Tick) + } + return thaw +} + +func (thaw *SingleServerThaw) Thawed() bool { + if Config.ServerCount == 1 { + return thaw.DefaultThaw.Thawed() + } + return true +} + +func (thaw *SingleServerThaw) Thaw() { + if Config.ServerCount == 1 { + thaw.DefaultThaw.Thaw() + } +} + +type DefaultThaw struct { + thawed int64 + sync.Mutex +} + +func NewDefaultThaw() *DefaultThaw { + thaw := &DefaultThaw{} + AddScheduledSecondTask(thaw.Tick) + return thaw +} + +// Decrement the thawed counter once a second until it goes cold +func (thaw *DefaultThaw) Tick() error { + thaw.Lock() + defer thaw.Unlock() + prior := thaw.thawed + if prior > 0 { + atomic.StoreInt64(&thaw.thawed, prior-1) + } + return nil +} + +func (thaw *DefaultThaw) Thawed() bool { + return thaw.thawed > 0 +} + +func (thaw *DefaultThaw) Thaw() { + atomic.StoreInt64(&thaw.thawed, 5) +} diff --git a/common/topic.go b/common/topic.go index f143b510..2ed673ec 100644 --- a/common/topic.go +++ b/common/topic.go @@ -192,6 +192,7 @@ func (topic *Topic) cacheRemove() { if tcache != nil { tcache.Remove(topic.ID) } + TopicListThaw.Thaw() } // TODO: Write a test for this @@ -259,6 +260,7 @@ func (topic *Topic) Like(score int, uid int) (err error) { // TODO: Implement this func (topic *Topic) Unlike(uid int) error { + topic.cacheRemove() return nil } diff --git a/common/topic_list.go b/common/topic_list.go index 976ab361..7945879b 100644 --- a/common/topic_list.go +++ b/common/topic_list.go @@ -52,6 +52,12 @@ func NewDefaultTopicList() (*DefaultTopicList, error) { } func (tList *DefaultTopicList) Tick() error { + //fmt.Println("TopicList.Tick") + if !TopicListThaw.Thawed() { + return nil + } + //fmt.Println("building topic list") + var oddLists = make(map[int]*TopicListHolder) var evenLists = make(map[int]*TopicListHolder) diff --git a/common/topic_store.go b/common/topic_store.go index 40b94b1c..da1610b7 100644 --- a/common/topic_store.go +++ b/common/topic_store.go @@ -113,6 +113,7 @@ func (mts *DefaultTopicStore) Reload(id int) error { } else { _ = mts.cache.Remove(id) } + TopicListThaw.Thaw() return err } diff --git a/common/user.go b/common/user.go index e932942b..f074635a 100644 --- a/common/user.go +++ b/common/user.go @@ -181,6 +181,7 @@ func (user *User) CacheRemove() { if ucache != nil { ucache.Remove(user.ID) } + TopicListThaw.Thaw() } func (user *User) Ban(duration time.Duration, issuedBy int) error { diff --git a/common/user_store.go b/common/user_store.go index 73623e2b..4e90429e 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -60,7 +60,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) { exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""), register: acc.Insert("users").Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""), - userCount: acc.SimpleCount("users", "", ""), + userCount: acc.Count("users").Prepare(), }, acc.FirstError() } @@ -244,6 +244,7 @@ func (mus *DefaultUserStore) Reload(id int) error { user.Init() _ = mus.cache.Set(user) + TopicListThaw.Thaw() return nil } @@ -270,12 +271,10 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri if err != ErrNoRows { return 0, ErrAccountExists } - salt, err := GenerateSafeString(SaltLength) if err != nil { return 0, err } - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password+salt), bcrypt.DefaultCost) if err != nil { return 0, err diff --git a/general_test.go b/general_test.go index ba627874..5bacbb10 100644 --- a/general_test.go +++ b/general_test.go @@ -69,6 +69,7 @@ func gloinit() (err error) { if err != nil { return errors.WithStack(err) } + common.TopicListThaw = common.NewSingleServerThaw() common.SwitchToTestDB() var ok bool diff --git a/main.go b/main.go index a58bf91e..7565d0d5 100644 --- a/main.go +++ b/main.go @@ -259,6 +259,7 @@ func main() { if err != nil { log.Fatal(err) } + common.TopicListThaw = common.NewSingleServerThaw() err = InitDatabase() if err != nil {