diff --git a/bot_routes.go b/bot_routes.go
index a9299dba..4e26f376 100644
--- a/bot_routes.go
+++ b/bot_routes.go
@@ -47,7 +47,7 @@ func routeSitemapXml(w http.ResponseWriter, r *http.Request) common.RouteError {
writeXMLHeader(w, r)
w.Write([]byte("\n"))
sitemapItem("sitemaps/topics.xml")
- sitemapItem("sitemaps/forums.xml")
+ //sitemapItem("sitemaps/forums.xml")
//sitemapItem("sitemaps/users.xml")
w.Write([]byte(""))
diff --git a/common/counters.go b/common/counters.go
index 8853dee5..87932509 100644
--- a/common/counters.go
+++ b/common/counters.go
@@ -2,7 +2,6 @@ package common
import (
"database/sql"
- "log"
"sync"
"sync/atomic"
@@ -28,6 +27,7 @@ func NewChunkedViewCounter() (*ChunkedViewCounter, error) {
}
AddScheduledFifteenMinuteTask(counter.Tick) // This is run once every fifteen minutes to match the frequency of the RouteViewCounter
//AddScheduledSecondTask(counter.Tick)
+ AddShutdownTask(counter.Tick)
return counter, acc.FirstError()
}
@@ -82,6 +82,7 @@ func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
}
AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second
//AddScheduledSecondTask(counter.Tick)
+ AddShutdownTask(counter.Tick)
return counter, acc.FirstError()
}
@@ -113,7 +114,7 @@ func (counter *DefaultRouteViewCounter) insertChunk(count int, route int) error
func (counter *DefaultRouteViewCounter) Bump(route int) {
// TODO: Test this check
- log.Print("counter.routeBuckets[route]: ", counter.routeBuckets[route])
+ debugLog("counter.routeBuckets[", route, "]: ", counter.routeBuckets[route])
if len(counter.routeBuckets) <= route {
return
}
@@ -157,41 +158,53 @@ func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
evenTopics: make(map[int]*RWMutexCounterBucket),
update: acc.Update("topics").Set("views = views + ?").Where("tid = ?").Prepare(),
}
- AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second
+ AddScheduledFifteenMinuteTask(counter.Tick) // Who knows how many topics we have queued up, we probably don't want this running too frequently
//AddScheduledSecondTask(counter.Tick)
+ AddShutdownTask(counter.Tick)
return counter, acc.FirstError()
}
func (counter *DefaultTopicViewCounter) Tick() error {
counter.oddLock.RLock()
- for topicID, topic := range counter.oddTopics {
+ oddTopics := counter.oddTopics
+ counter.oddLock.RUnlock()
+ for topicID, topic := range oddTopics {
var count int
topic.RLock()
count = topic.counter
topic.RUnlock()
+ // TODO: Only delete the bucket when it's zero to avoid hitting popular topics?
+ counter.oddLock.Lock()
+ delete(counter.oddTopics, topicID)
+ counter.oddLock.Unlock()
err := counter.insertChunk(count, topicID)
if err != nil {
return err
}
}
- counter.oddLock.RUnlock()
counter.evenLock.RLock()
- for topicID, topic := range counter.evenTopics {
+ evenTopics := counter.evenTopics
+ counter.evenLock.RUnlock()
+ for topicID, topic := range evenTopics {
var count int
topic.RLock()
count = topic.counter
topic.RUnlock()
+ // TODO: Only delete the bucket when it's zero to avoid hitting popular topics?
+ counter.evenLock.Lock()
+ delete(counter.evenTopics, topicID)
+ counter.evenLock.Unlock()
err := counter.insertChunk(count, topicID)
if err != nil {
return err
}
}
- counter.evenLock.RUnlock()
return nil
}
+// TODO: Optimise this further. E.g. Using IN() on every one view topic. Rinse and repeat for two views, three views, four views and five views.
func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) error {
if count == 0 {
return nil
@@ -204,9 +217,9 @@ func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) erro
func (counter *DefaultTopicViewCounter) Bump(topicID int) {
// Is the ID even?
if topicID%2 == 0 {
- counter.evenLock.Lock()
+ counter.evenLock.RLock()
topic, ok := counter.evenTopics[topicID]
- counter.evenLock.Unlock()
+ counter.evenLock.RUnlock()
if ok {
topic.Lock()
topic.counter++
@@ -219,9 +232,9 @@ func (counter *DefaultTopicViewCounter) Bump(topicID int) {
return
}
- counter.oddLock.Lock()
+ counter.oddLock.RLock()
topic, ok := counter.oddTopics[topicID]
- counter.oddLock.Unlock()
+ counter.oddLock.RUnlock()
if ok {
topic.Lock()
topic.counter++
diff --git a/common/tasks.go b/common/tasks.go
index 513a13de..0069bb19 100644
--- a/common/tasks.go
+++ b/common/tasks.go
@@ -21,6 +21,7 @@ type TaskStmts struct {
var ScheduledSecondTasks []func() error
var ScheduledFifteenMinuteTasks []func() error
+var ShutdownTasks []func() error
var taskStmts TaskStmts
var lastSync time.Time
@@ -45,6 +46,11 @@ func AddScheduledFifteenMinuteTask(task func() error) {
ScheduledFifteenMinuteTasks = append(ScheduledFifteenMinuteTasks, task)
}
+// AddShutdownTask is not concurrency safe
+func AddShutdownTask(task func() error) {
+ ShutdownTasks = append(ShutdownTasks, task)
+}
+
// TODO: Use AddScheduledSecondTask
func HandleExpiredScheduledGroups() error {
rows, err := taskStmts.getExpiredScheduledGroups.Query()
diff --git a/gen_router.go b/gen_router.go
index 3d0ee519..8132033d 100644
--- a/gen_router.go
+++ b/gen_router.go
@@ -72,6 +72,8 @@ var RouteMap = map[string]interface{}{
"routeUnban": routeUnban,
"routeActivate": routeActivate,
"routeIps": routeIps,
+ "routeDynamic": routeDynamic,
+ "routeUploads": routeUploads,
}
// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS
@@ -133,6 +135,8 @@ var routeMapEnum = map[string]int{
"routeUnban": 54,
"routeActivate": 55,
"routeIps": 56,
+ "routeDynamic": 57,
+ "routeUploads": 58,
}
var reverseRouteMapEnum = map[int]string{
0: "routeAPI",
@@ -192,6 +196,8 @@ var reverseRouteMapEnum = map[int]string{
54: "routeUnban",
55: "routeActivate",
56: "routeIps",
+ 57: "routeDynamic",
+ 58: "routeUploads",
}
// TODO: Stop spilling these into the package scope?
@@ -794,6 +800,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
common.NotFound(w,req)
return
}
+ common.RouteViewCounter.Bump(58)
req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this?
router.UploadHandler(w,req) // TODO: Count these views
@@ -837,8 +844,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.RUnlock()
if ok {
+ common.RouteViewCounter.Bump(57) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData
- err = handle(w,req,user) // TODO: Count these views
+ err = handle(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
}
diff --git a/main.go b/main.go
index e6128765..82c76032 100644
--- a/main.go
+++ b/main.go
@@ -234,6 +234,14 @@ func main() {
}
}
+ var runTasks = func(tasks []func() error) {
+ for _, task := range tasks {
+ if task() != nil {
+ common.LogError(err)
+ }
+ }
+ }
+
// Run this goroutine once a second
secondTicker := time.NewTicker(1 * time.Second)
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
@@ -242,22 +250,16 @@ func main() {
for {
select {
case <-secondTicker.C:
- //log.Print("Running the second ticker")
// TODO: Add a plugin hook here
+ runTasks(common.ScheduledSecondTasks)
- for _, task := range common.ScheduledSecondTasks {
- if task() != nil {
- common.LogError(err)
- }
- }
-
+ // TODO: Stop hard-coding this
err := common.HandleExpiredScheduledGroups()
if err != nil {
common.LogError(err)
}
// TODO: Handle delayed moderation tasks
- // TODO: Handle the daily clean-up. Move this to a 24 hour task?
// Sync with the database, if there are any changes
err = common.HandleServerSync()
@@ -273,18 +275,15 @@ func main() {
// TODO: Add a plugin hook here
case <-fifteenMinuteTicker.C:
// TODO: Add a plugin hook here
-
- for _, task := range common.ScheduledFifteenMinuteTasks {
- if task() != nil {
- common.LogError(err)
- }
- }
+ runTasks(common.ScheduledFifteenMinuteTasks)
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
// TODO: Publish scheduled posts.
// TODO: Add a plugin hook here
}
+
+ // TODO: Handle the daily clean-up.
}
}()
@@ -329,6 +328,7 @@ func main() {
go func() {
sig := <-sigs
// TODO: Gracefully shutdown the HTTP server
+ runTasks(common.ShutdownTasks)
log.Fatal("Received a signal to shutdown: ", sig)
}()
diff --git a/router_gen/main.go b/router_gen/main.go
index 72db1971..c4b272c1 100644
--- a/router_gen/main.go
+++ b/router_gen/main.go
@@ -150,6 +150,9 @@ func main() {
}`
}
+ // Stubs for us to refer to these routes through
+ mapIt("routeDynamic")
+ mapIt("routeUploads")
tmplVars.AllRouteNames = allRouteNames
tmplVars.AllRouteMap = allRouteMap
@@ -294,6 +297,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
common.NotFound(w,req)
return
}
+ common.RouteViewCounter.Bump({{.AllRouteMap.routeUploads}})
req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this?
router.UploadHandler(w,req) // TODO: Count these views
@@ -337,8 +341,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.RUnlock()
if ok {
+ common.RouteViewCounter.Bump({{.AllRouteMap.routeDynamic}}) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData
- err = handle(w,req,user) // TODO: Count these views
+ err = handle(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
}
diff --git a/routes.go b/routes.go
index bd39a64e..24efdc80 100644
--- a/routes.go
+++ b/routes.go
@@ -40,6 +40,12 @@ func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
}
+// Temporary stubs for view tracking
+func routeDynamic() {
+}
+func routeUploads() {
+}
+
// GET functions
func routeStatic(w http.ResponseWriter, r *http.Request) {
file, ok := common.StaticFiles.Get(r.URL.Path)
@@ -613,6 +619,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
if err != nil {
return common.InternalError(err, w, r)
}
+ common.TopicViewCounter.Bump(topic.ID) // TODO Move this into the router?
return nil
}
diff --git a/template_list.go b/template_list.go
index 0d43f1c6..4f08e252 100644
--- a/template_list.go
+++ b/template_list.go
@@ -601,8 +601,7 @@ var profile_21 = []byte(`
-
Comments