This commit is contained in:
Philipp Heckel 2021-11-02 14:08:21 -04:00
parent b775e6dfce
commit 67922b0ae5
8 changed files with 175 additions and 6 deletions

61
server/cache.go Normal file
View 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
}

View file

@ -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>

View file

@ -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)

View file

@ -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,