1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
/*
Copyright (c) 2019 Ben Morrison (gbmor)
This file is part of Getwtxt.
Getwtxt is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Getwtxt is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Getwtxt. If not, see <https://www.gnu.org/licenses/>.
*/
package svc // import "git.sr.ht/~gbmor/getwtxt/svc"
import (
"log"
"time"
"github.com/fsnotify/fsnotify"
)
// Functions and types in this file pertain
// to periodic, regular actions.
// This is a wrapper for a *time.Ticker
// that adds another channel. It's used
// to signal to the ticker goroutines
// that they should stop the tickers
// and exit.
type tick struct {
isDB bool
t *time.Ticker
exit chan struct{}
}
// Creates a new instance of a tick
func initTicker(db bool, interval time.Duration) *tick {
return &tick{
isDB: db,
t: time.NewTicker(interval),
exit: make(chan struct{}, 1),
}
}
// Sends the signal to stop the tickers
// and for their respective goroutines
// to exit.
func killTickers() {
ct := <-cTickC
dt := <-dbTickC
ct.exit <- struct{}{}
dt.exit <- struct{}{}
}
// Waits for a signal from the database
// *tick. Either stops the ticker and
// kills the goroutine or it will
// update cache / push the DB to disk
func dataTimer(tkr *tick) {
for {
select {
case signal := <-tkr.t.C:
if tkr.isDB {
errLog("", pushDB())
log.Printf("Database push took: %v\n", time.Since(signal))
continue
}
cacheUpdate()
log.Printf("Cache update took: %v\n", time.Since(signal))
case <-tkr.exit:
tkr.t.Stop()
return
}
}
}
// Called when a change is detected in the
// configuration file. Closes log file,
// closes database connection, stops all
// tickers, then binds new configuration
// values, opens new log file, connects to
// new database, and starts new cache and
// database tickers.
func reInit(e fsnotify.Event) {
log.Printf("%v. Reloading...\n", e.String())
if !confObj.StdoutLogging {
closeLog <- struct{}{}
}
killTickers()
bindConfig()
initLogging()
initPersistence()
}
// Starts the tickers that periodically:
// - pull new user statuses into cache
// - push cached data to disk
func initPersistence() {
confObj.Mu.RLock()
cacheTkr := initTicker(false, confObj.CacheInterval)
dbTkr := initTicker(true, confObj.DBInterval)
confObj.Mu.RUnlock()
go dataTimer(cacheTkr)
go dataTimer(dbTkr)
dbTickC <- dbTkr
cTickC <- cacheTkr
}
|