better fetch
This commit is contained in:
parent
3fdb38d3a0
commit
67c3090434
1 changed files with 46 additions and 5 deletions
51
main.go
51
main.go
|
|
@ -28,9 +28,10 @@ var staticFiles embed.FS
|
||||||
var templateFiles embed.FS
|
var templateFiles embed.FS
|
||||||
|
|
||||||
const (
|
const (
|
||||||
geojsonURL = "https://regieessencequebec.ca/stations.geojson.gz"
|
geojsonURL = "https://regieessencequebec.ca/stations.geojson.gz"
|
||||||
defaultPort = "8080"
|
defaultPort = "8080"
|
||||||
pollInterval = 5 * time.Minute
|
pollInterval = 5 * time.Minute
|
||||||
|
fetchCooldown = 1 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// GeoJSON structures matching the upstream format.
|
// GeoJSON structures matching the upstream format.
|
||||||
|
|
@ -88,6 +89,13 @@ var (
|
||||||
cachedResp *StationsResponse
|
cachedResp *StationsResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Fetch cooldown state: prevents request-triggered fetches from
|
||||||
|
// firing more often than fetchCooldown.
|
||||||
|
var (
|
||||||
|
fetchMu sync.Mutex
|
||||||
|
lastFetchTime time.Time
|
||||||
|
)
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
|
|
||||||
// tmpl is the parsed template set, loaded once at startup.
|
// tmpl is the parsed template set, loaded once at startup.
|
||||||
|
|
@ -231,6 +239,8 @@ func handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// handleMapPage renders the full map page (or just the content block for htmx).
|
// handleMapPage renders the full map page (or just the content block for htmx).
|
||||||
func handleMapPage(w http.ResponseWriter, r *http.Request) {
|
func handleMapPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
triggerFetch()
|
||||||
|
|
||||||
cacheMu.RLock()
|
cacheMu.RLock()
|
||||||
resp := cachedResp
|
resp := cachedResp
|
||||||
cacheMu.RUnlock()
|
cacheMu.RUnlock()
|
||||||
|
|
@ -718,17 +728,48 @@ func initDB(path string) (*sql.DB, error) {
|
||||||
|
|
||||||
// poller fetches immediately then every pollInterval.
|
// poller fetches immediately then every pollInterval.
|
||||||
func poller() {
|
func poller() {
|
||||||
fetchAndStore()
|
safeFetchAndStore()
|
||||||
ticker := time.NewTicker(pollInterval)
|
ticker := time.NewTicker(pollInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
fetchAndStore()
|
safeFetchAndStore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safeFetchAndStore calls fetchAndStore and recovers from any panic so the
|
||||||
|
// poller goroutine stays alive.
|
||||||
|
func safeFetchAndStore() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("panic in fetchAndStore: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fetchAndStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
// triggerFetch kicks off a background fetchAndStore if at least fetchCooldown
|
||||||
|
// has elapsed since the last fetch attempt. It returns immediately and never
|
||||||
|
// blocks the caller.
|
||||||
|
func triggerFetch() {
|
||||||
|
fetchMu.Lock()
|
||||||
|
if time.Since(lastFetchTime) < fetchCooldown {
|
||||||
|
fetchMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastFetchTime = time.Now()
|
||||||
|
fetchMu.Unlock()
|
||||||
|
go safeFetchAndStore()
|
||||||
|
}
|
||||||
|
|
||||||
// fetchAndStore fetches upstream data, updates the in-memory cache, and
|
// fetchAndStore fetches upstream data, updates the in-memory cache, and
|
||||||
// persists a snapshot to SQLite if the data has a new generated_at value.
|
// persists a snapshot to SQLite if the data has a new generated_at value.
|
||||||
func fetchAndStore() {
|
func fetchAndStore() {
|
||||||
|
// Reset cooldown so poller-initiated fetches also prevent redundant
|
||||||
|
// request-triggered fetches.
|
||||||
|
fetchMu.Lock()
|
||||||
|
lastFetchTime = time.Now()
|
||||||
|
fetchMu.Unlock()
|
||||||
|
|
||||||
resp, err := fetchAndParse()
|
resp, err := fetchAndParse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("fetch error: %v", err)
|
log.Printf("fetch error: %v", err)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue