|
@@ -2,9 +2,12 @@ package main
|
|
|
|
|
|
import (
|
|
|
"database/sql"
|
|
|
+ "fmt"
|
|
|
"log"
|
|
|
"os"
|
|
|
"strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
)
|
|
@@ -12,8 +15,9 @@ import (
|
|
|
// Make sure the go-sqlite3 is here. Keep the db related things together.
|
|
|
|
|
|
type DBData struct {
|
|
|
- DB *sql.DB
|
|
|
- User string
|
|
|
+ DB *sql.DB
|
|
|
+ User string
|
|
|
+ LockValue string
|
|
|
}
|
|
|
|
|
|
func (db *DBData) Open(filename string) {
|
|
@@ -64,7 +68,11 @@ func (db *DBData) Create() {
|
|
|
*/
|
|
|
}
|
|
|
|
|
|
-func (db *DBData) Lock(value string) string {
|
|
|
+func (db *DBData) Lock(timeout int) bool {
|
|
|
+ var now time.Time = time.Now()
|
|
|
+ var value string = fmt.Sprintf("%d,%d", os.Getpid(), now.Unix())
|
|
|
+
|
|
|
+RetryLock:
|
|
|
_, err := db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
|
|
|
value, LOCK_USERNAME, LOCK_SETTING, LOCK_CLEAR)
|
|
|
if err != nil {
|
|
@@ -77,15 +85,57 @@ func (db *DBData) Lock(value string) string {
|
|
|
if err != nil {
|
|
|
log.Println("Lock Scan", err)
|
|
|
}
|
|
|
- log.Printf("Lock(%s) = %s\n", value, result)
|
|
|
- return result
|
|
|
+ // I need to do further checking here -- to make sure the lock isn't stale.
|
|
|
+
|
|
|
+ if result == value {
|
|
|
+ // Success! We've got the lock!
|
|
|
+ log.Printf("Lock [%s]\n", value)
|
|
|
+ db.LockValue = value
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ // We failed. There's already a lock present. Check if it is stale.
|
|
|
+ var lockSet string
|
|
|
+ var pos int
|
|
|
+ var howOld time.Duration
|
|
|
+ pos = strings.Index(result, ",")
|
|
|
+ if pos != -1 {
|
|
|
+ lockSet = result[pos+1:]
|
|
|
+ var unixtime int64
|
|
|
+ unixtime, err = strconv.ParseInt(lockSet, 10, 64)
|
|
|
+ if err == nil {
|
|
|
+ var lockTime time.Time = time.Unix(unixtime, 0)
|
|
|
+ howOld = now.Sub(lockTime)
|
|
|
+ log.Printf("Lock %s [%0.2f seconds old]\n", result, howOld.Seconds())
|
|
|
+ if howOld.Seconds() > 60 {
|
|
|
+ // Lock expired
|
|
|
+ log.Printf("Lock %s [%0.2f seconds old] -- Expiring\n", result, howOld.Seconds())
|
|
|
+ db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
|
|
|
+ LOCK_CLEAR, LOCK_USERNAME, LOCK_SETTING, result)
|
|
|
+ goto RetryLock
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.Printf("Can't find , in [%s]\n", result)
|
|
|
+ }
|
|
|
+
|
|
|
+ time.Sleep(time.Duration(50) * time.Millisecond)
|
|
|
+ howOld = time.Until(now) // now.Sub(time.Now())
|
|
|
+ if int(-howOld.Seconds()) > timeout {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ goto RetryLock
|
|
|
}
|
|
|
|
|
|
-func (db *DBData) Unlock(value string) bool {
|
|
|
+func (db *DBData) Unlock() bool {
|
|
|
+ if db.LockValue == "" {
|
|
|
+ // Nothing to unlock -- we didn't get the lock.
|
|
|
+ return true
|
|
|
+ }
|
|
|
_, err := db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
|
|
|
- LOCK_CLEAR, LOCK_USERNAME, LOCK_SETTING, value)
|
|
|
+ LOCK_CLEAR, LOCK_USERNAME, LOCK_SETTING, db.LockValue)
|
|
|
if err != nil {
|
|
|
- log.Printf("Unlock(%s) : %s\n", value, err)
|
|
|
+ log.Printf("Unlock(%s) : %s\n", db.LockValue, err)
|
|
|
}
|
|
|
row := db.DB.QueryRow("SELECT value FROM settings WHERE username=? AND setting=?;",
|
|
|
LOCK_USERNAME, LOCK_SETTING)
|
|
@@ -94,7 +144,11 @@ func (db *DBData) Unlock(value string) bool {
|
|
|
if err != nil {
|
|
|
log.Println("Unlock Scan", err)
|
|
|
}
|
|
|
- log.Printf("Unlock(%s) = %s\n", value, result)
|
|
|
+ if result != LOCK_CLEAR {
|
|
|
+ log.Printf("Unlock Failed (LockValue %s) Result %s (should be %s)\n", db.LockValue, result, LOCK_CLEAR)
|
|
|
+ } else {
|
|
|
+ log.Println("Unlocked.")
|
|
|
+ }
|
|
|
return result == LOCK_CLEAR
|
|
|
}
|
|
|
|
|
@@ -183,18 +237,15 @@ type MonthStats struct {
|
|
|
|
|
|
func (db *DBData) ExpireScores(month_first_t int64) {
|
|
|
// step 1: acquire lock
|
|
|
- // ... step 0: find working lock strategy in go. :(
|
|
|
- // lockfile := lockedfile.Create("db.lock")
|
|
|
- // defer lockfile.Close()
|
|
|
- var pid string = strconv.Itoa(os.Getpid())
|
|
|
|
|
|
- l := db.Lock(pid)
|
|
|
+ l := db.Lock(5)
|
|
|
|
|
|
- if l == pid {
|
|
|
- log.Printf("Lock worked!\n")
|
|
|
- defer db.Unlock(pid)
|
|
|
+ if l {
|
|
|
+ // log.Println("Locked")
|
|
|
+ defer db.Unlock()
|
|
|
} else {
|
|
|
- log.Printf("Lock failed [%s]\n", l)
|
|
|
+ log.Println("Lock failed")
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
// var Monthly map[MonthUser]MonthStats = make(map[MonthUser]MonthStats)
|