mirror of
https://github.com/wahyd4/golink.git
synced 2026-08-08 21:01:05 +10:00
cmd/golink: add stat loading and saving to DB
add new DB.LoadStats and DB.SaveStats methods. DB.SaveSaveStats takes an incremental count of clicks since the last call. This is a noop for FileDB (the full count is still saved in the Link struct on disk), but will allow for the SqliteDB to store stats in a separate table with timestamps to display only recent clicks. Change-Id: I18c7db871854923b873ae100c844940242a32bb3
This commit is contained in:
@@ -20,6 +20,10 @@ type Link struct {
|
||||
Clicks int `json:",omitempty"` // number of times this link has been served
|
||||
}
|
||||
|
||||
// ClickStats is the number of clicks a set of links have received in a given
|
||||
// time period. It is keyed by link short name, with values of total clicks.
|
||||
type ClickStats map[string]int
|
||||
|
||||
// DB provides storage for Links.
|
||||
type DB interface {
|
||||
// LoadAll returns all stored Links.
|
||||
@@ -36,6 +40,14 @@ type DB interface {
|
||||
|
||||
// Save saves a Link.
|
||||
Save(*Link) error
|
||||
|
||||
// LoadStats returns click stats for links.
|
||||
LoadStats() (ClickStats, error)
|
||||
|
||||
// SaveStats records click stats for links. The provided map includes
|
||||
// incremental clicks that have occurred since the last time SaveStats
|
||||
// was called.
|
||||
SaveStats(ClickStats) error
|
||||
}
|
||||
|
||||
// FileDB stores Links in JSON files on disk.
|
||||
@@ -120,3 +132,36 @@ func (f *FileDB) Save(link *Link) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileDB) LoadStats() (ClickStats, error) {
|
||||
links, err := db.LoadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats := make(ClickStats)
|
||||
for _, link := range links {
|
||||
if link.Clicks > 0 {
|
||||
stats[link.Short] = link.Clicks
|
||||
}
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (f *FileDB) SaveStats(stats ClickStats) error {
|
||||
for short, clicks := range stats {
|
||||
if clicks <= 0 {
|
||||
continue
|
||||
}
|
||||
link, err := f.Load(short)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link.Clicks += clicks
|
||||
if err := f.Save(link); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ var (
|
||||
|
||||
var stats struct {
|
||||
mu sync.Mutex
|
||||
clicks map[string]int // short link -> number of times visited
|
||||
clicks ClickStats // short link -> number of times visited
|
||||
|
||||
// dirty identifies short links with clicks that have not yet been stored.
|
||||
dirty map[string]bool
|
||||
// dirty identifies short link clicks that have not yet been stored.
|
||||
dirty ClickStats
|
||||
}
|
||||
|
||||
//go:embed link-snapshot.json
|
||||
@@ -140,20 +140,16 @@ func init() {
|
||||
|
||||
// initStats initializes the in-memory stats counter with counts from db.
|
||||
func initStats() error {
|
||||
links, err := db.LoadAll()
|
||||
stats.mu.Lock()
|
||||
defer stats.mu.Unlock()
|
||||
|
||||
clicks, err := db.LoadStats()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stats.mu.Lock()
|
||||
defer stats.mu.Unlock()
|
||||
|
||||
stats.clicks = make(map[string]int)
|
||||
for _, link := range links {
|
||||
if link.Clicks > 0 {
|
||||
stats.clicks[link.Short] = link.Clicks
|
||||
}
|
||||
}
|
||||
stats.clicks = clicks
|
||||
stats.dirty = make(ClickStats)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -163,19 +159,10 @@ func flushStats() error {
|
||||
stats.mu.Lock()
|
||||
defer stats.mu.Unlock()
|
||||
|
||||
for short := range stats.dirty {
|
||||
link, err := db.Load(short)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if link.Clicks != stats.clicks[short] {
|
||||
link.Clicks = stats.clicks[short]
|
||||
if err := db.Save(link); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(stats.dirty, short)
|
||||
if err := db.SaveStats(stats.dirty); err != nil {
|
||||
return err
|
||||
}
|
||||
stats.dirty = make(ClickStats)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -260,13 +247,13 @@ func serveGo(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
stats.mu.Lock()
|
||||
if stats.clicks == nil {
|
||||
stats.clicks = make(map[string]int)
|
||||
stats.clicks = make(ClickStats)
|
||||
}
|
||||
stats.clicks[link.Short]++
|
||||
if stats.dirty == nil {
|
||||
stats.dirty = make(map[string]bool)
|
||||
stats.dirty = make(ClickStats)
|
||||
}
|
||||
stats.dirty[link.Short] = true
|
||||
stats.dirty[link.Short]++
|
||||
stats.mu.Unlock()
|
||||
|
||||
target, err := expandLink(link.Long, expandEnv{Now: time.Now().UTC(), Path: remainder})
|
||||
|
||||
Reference in New Issue
Block a user