Cache
This commit is contained in:
parent
b775e6dfce
commit
67922b0ae5
8 changed files with 175 additions and 6 deletions
61
server/cache.go
Normal file
61
server/cache.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
_ "github.com/mattn/go-sqlite3" // SQLite driver
|
||||
)
|
||||
|
||||
const (
|
||||
createTableQuery = `CREATE TABLE IF NOT EXISTS messages (
|
||||
id VARCHAR(20) PRIMARY KEY,
|
||||
time INT NOT NULL,
|
||||
topic VARCHAR(64) NOT NULL,
|
||||
message VARCHAR(1024) NOT NULL
|
||||
)`
|
||||
insertQuery = `INSERT INTO messages (id, time, topic, message) VALUES (?, ?, ?, ?)`
|
||||
pruneOlderThanQuery = `DELETE FROM messages WHERE time < ?`
|
||||
)
|
||||
|
||||
type cache struct {
|
||||
db *sql.DB
|
||||
insert *sql.Stmt
|
||||
prune *sql.Stmt
|
||||
}
|
||||
|
||||
func newCache(filename string) (*cache, error) {
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := db.Exec(createTableQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
insert, err := db.Prepare(insertQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prune, err := db.Prepare(pruneOlderThanQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cache{
|
||||
db: db,
|
||||
insert: insert,
|
||||
prune: prune,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *cache) Load() (map[string]*topic, error) {
|
||||
|
||||
}
|
||||
|
||||
func (c *cache) Add(m *message) error {
|
||||
_, err := c.insert.Exec(m.ID, m.Time, m.Topic, m.Message)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *cache) Prune(olderThan time.Duration) error {
|
||||
_, err := c.prune.Exec(time.Now().Add(-1 * olderThan).Unix())
|
||||
return err
|
||||
}
|
|
@ -191,7 +191,7 @@
|
|||
|
||||
<p>
|
||||
The web server does not log or otherwise store request paths, remote IP addresses or even topics or messages,
|
||||
aside from a short on-disk cache (up to a day) to support the <tt>since=</tt> feature and service restarts.
|
||||
aside from a short on-disk cache (up to a day) to support service restarts.
|
||||
</p>
|
||||
|
||||
<center id="ironicCenterTagDontFreakOut"><i>Made with ❤️ by <a href="https://heckel.io">Philipp C. Heckel</a></i></center>
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
)
|
||||
|
||||
// TODO add "max messages in a topic" limit
|
||||
// TODO implement persistence
|
||||
// TODO implement "since=<ID>"
|
||||
|
||||
// Server is the main server
|
||||
|
@ -33,6 +32,7 @@ type Server struct {
|
|||
visitors map[string]*visitor
|
||||
firebase subscriber
|
||||
messages int64
|
||||
cache *cache
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -78,14 +78,32 @@ func New(conf *config.Config) (*Server, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
cache, err := maybeCreateCache(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topics := make(map[string]*topic)
|
||||
if cache != nil {
|
||||
if topics, err = cache.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &Server{
|
||||
config: conf,
|
||||
cache: cache,
|
||||
firebase: firebaseSubscriber,
|
||||
topics: make(map[string]*topic),
|
||||
topics: topics,
|
||||
visitors: make(map[string]*visitor),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func maybeCreateCache(conf *config.Config) (*cache, error) {
|
||||
if conf.CacheFile == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return newCache(conf.CacheFile)
|
||||
}
|
||||
|
||||
func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
||||
fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(conf.FirebaseKeyFile))
|
||||
if err != nil {
|
||||
|
@ -180,9 +198,13 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.Publish(newDefaultMessage(t.id, string(b))); err != nil {
|
||||
m := newDefaultMessage(t.id, string(b))
|
||||
if err := t.Publish(m); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.cache != nil {
|
||||
s.cache.Add(m)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
|
||||
s.mu.Lock()
|
||||
s.messages++
|
||||
|
@ -337,6 +359,13 @@ func (s *Server) updateStatsAndExpire() {
|
|||
}
|
||||
}
|
||||
|
||||
// Prune cache
|
||||
if s.cache != nil {
|
||||
if err := s.cache.Prune(s.config.MessageBufferDuration); err != nil {
|
||||
log.Printf("error pruning cache: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Prune old messages, remove subscriptions without subscribers
|
||||
for _, t := range s.topics {
|
||||
t.Prune(s.config.MessageBufferDuration)
|
||||
|
|
|
@ -29,6 +29,7 @@ func newTopic(id string) *topic {
|
|||
return &topic{
|
||||
id: id,
|
||||
subscribers: make(map[int]subscriber),
|
||||
messages: make([]*message, 0),
|
||||
last: time.Now(),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue