Added graceful shutdown for SQLite logging

pull/410/head
AvSquirrel 2016-05-02 05:03:21 +00:00
rodzic 2dd8a14d62
commit 24a6da3da9
2 zmienionych plików z 88 dodań i 17 usunięć

Wyświetl plik

@ -43,6 +43,10 @@ type StratuxStartup struct {
Fill string Fill string
} }
var dataLogStarted bool
var dataLogReadyToWrite bool
//var dataLogInShutdown bool
var stratuxStartupID int64 var stratuxStartupID int64
var dataLogTimestamps []StratuxTimestamp var dataLogTimestamps []StratuxTimestamp
var dataLogCurTimestamp int64 // Current timestamp bucket. This is an index on dataLogTimestamps which is not necessarily the db id. var dataLogCurTimestamp int64 // Current timestamp bucket. This is an index on dataLogTimestamps which is not necessarily the db id.
@ -350,11 +354,13 @@ type DataLogRow struct {
var dataLogChan chan DataLogRow var dataLogChan chan DataLogRow
var shutdownDataLog chan bool var shutdownDataLog chan bool
var shutdownDataLogWriter chan bool
var dataLogWriteChan chan DataLogRow var dataLogWriteChan chan DataLogRow
func dataLogWriter(db *sql.DB) { func dataLogWriter(db *sql.DB) {
dataLogWriteChan = make(chan DataLogRow, 10240) dataLogWriteChan = make(chan DataLogRow, 10240)
shutdownDataLogWriter = make(chan bool)
// The write queue. As data comes in via dataLogChan, it is timestamped and stored. // The write queue. As data comes in via dataLogChan, it is timestamped and stored.
// When writeTicker comes up, the queue is emptied. // When writeTicker comes up, the queue is emptied.
writeTicker := time.NewTicker(10 * time.Second) writeTicker := time.NewTicker(10 * time.Second)
@ -363,6 +369,7 @@ func dataLogWriter(db *sql.DB) {
select { select {
case r := <-dataLogWriteChan: case r := <-dataLogWriteChan:
// Accept timestamped row. // Accept timestamped row.
//log.Printf("Accepting timestamped row from dataLogWriteChan\n")
rowsQueuedForWrite = append(rowsQueuedForWrite, r) rowsQueuedForWrite = append(rowsQueuedForWrite, r)
case <-writeTicker.C: case <-writeTicker.C:
// for i := 0; i < 1000; i++ { // for i := 0; i < 1000; i++ {
@ -370,9 +377,9 @@ func dataLogWriter(db *sql.DB) {
// } // }
timeStart := stratuxClock.Time timeStart := stratuxClock.Time
nRows := len(rowsQueuedForWrite) nRows := len(rowsQueuedForWrite)
if globalSettings.DEBUG { //if globalSettings.DEBUG {
log.Printf("Writing %d rows\n", nRows) log.Printf("Writing %d rows\n", nRows)
} //}
// Write the buffered rows. This will block while it is writing. // Write the buffered rows. This will block while it is writing.
// Save the names of the tables affected so that we can run bulkInsert() on after the insertData() calls. // Save the names of the tables affected so that we can run bulkInsert() on after the insertData() calls.
tblsAffected := make(map[string]bool) tblsAffected := make(map[string]bool)
@ -394,18 +401,26 @@ func dataLogWriter(db *sql.DB) {
tx.Commit() tx.Commit()
rowsQueuedForWrite = make([]DataLogRow, 0) // Zero the queue. rowsQueuedForWrite = make([]DataLogRow, 0) // Zero the queue.
timeElapsed := stratuxClock.Since(timeStart) timeElapsed := stratuxClock.Since(timeStart)
if globalSettings.DEBUG { //if globalSettings.DEBUG {
rowsPerSecond := float64(nRows) / float64(timeElapsed.Seconds()) rowsPerSecond := float64(nRows) / float64(timeElapsed.Seconds())
log.Printf("Writing finished. %d rows in %.2f seconds (%.1f rows per second).\n", nRows, float64(timeElapsed.Seconds()), rowsPerSecond) log.Printf("Writing finished. %d rows in %.2f seconds (%.1f rows per second).\n", nRows, float64(timeElapsed.Seconds()), rowsPerSecond)
} //}
if timeElapsed.Seconds() > 10.0 { if timeElapsed.Seconds() > 10.0 {
log.Printf("WARNING! SQLite logging is behind. Last write took %.1f seconds.\n", float64(timeElapsed.Seconds())) log.Printf("WARNING! SQLite logging is behind. Last write took %.1f seconds.\n", float64(timeElapsed.Seconds()))
} }
case <-shutdownDataLogWriter: // Received a message on the channel (anything). Graceful shutdown (defer statement).
log.Printf("dataLogWriter() received shutdown message with len(dataLogWriteChan) = %d and rowsQueuedForWrite = %d\n", len(dataLogWriteChan), len(rowsQueuedForWrite))
shutdownDataLog <- true
return
} }
} }
log.Printf("dataLogWriter() shutting down\n")
} }
func dataLog() { func dataLog() {
dataLogStarted = true
//dataLogInShutdown = false
log.Printf("dataLog started\n")
dataLogChan = make(chan DataLogRow, 10240) dataLogChan = make(chan DataLogRow, 10240)
shutdownDataLog = make(chan bool) shutdownDataLog = make(chan bool)
dataLogTimestamps = make([]StratuxTimestamp, 0) dataLogTimestamps = make([]StratuxTimestamp, 0)
@ -430,7 +445,12 @@ func dataLog() {
if err != nil { if err != nil {
log.Printf("sql.Open(): %s\n", err.Error()) log.Printf("sql.Open(): %s\n", err.Error())
} }
defer db.Close()
defer func() {
db.Close()
dataLogStarted = false
log.Printf("dataLog() dB is now closed\n")
}()
_, err = db.Exec("PRAGMA journal_mode=WAL") _, err = db.Exec("PRAGMA journal_mode=WAL")
if err != nil { if err != nil {
@ -441,6 +461,7 @@ func dataLog() {
log.Printf("db.Exec('PRAGMA journal_mode=WAL') err: %s\n", err.Error()) log.Printf("db.Exec('PRAGMA journal_mode=WAL') err: %s\n", err.Error())
} }
log.Printf("Starting dataLogWriter\n")
go dataLogWriter(db) go dataLogWriter(db)
// Do we need to create the database? // Do we need to create the database?
@ -459,6 +480,8 @@ func dataLog() {
// The first entry to be created is the "startup" entry. // The first entry to be created is the "startup" entry.
stratuxStartupID = insertData(StratuxStartup{}, "startup", db, 0) stratuxStartupID = insertData(StratuxStartup{}, "startup", db, 0)
dataLogReadyToWrite = true
log.Printf("Entering dataLog read loop\n")
for { for {
select { select {
case r := <-dataLogChan: case r := <-dataLogChan:
@ -470,9 +493,12 @@ func dataLog() {
// Queue it for the scheduled write. // Queue it for the scheduled write.
dataLogWriteChan <- r dataLogWriteChan <- r
case <-shutdownDataLog: // Received a message on the channel (anything). Graceful shutdown (defer statement). case <-shutdownDataLog: // Received a message on the channel (anything). Graceful shutdown (defer statement).
//dataLogStarted = false // moved to defer statement
log.Printf("dataLog() received shutdown message\n")
return return
} }
} }
log.Printf("dataLog() shutting down\n")
} }
/* /*
@ -496,49 +522,88 @@ func setDataLogTimeWithGPS(sit SituationData) {
} }
func logSituation() { func logSituation() {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "mySituation", data: mySituation} dataLogChan <- DataLogRow{tbl: "mySituation", data: mySituation}
} }
} }
func logStatus() { func logStatus() {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "status", data: globalStatus} dataLogChan <- DataLogRow{tbl: "status", data: globalStatus}
} }
} }
func logSettings() { func logSettings() {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings} dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings}
} }
} }
func logTraffic(ti TrafficInfo) { func logTraffic(ti TrafficInfo) {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "traffic", data: ti} dataLogChan <- DataLogRow{tbl: "traffic", data: ti}
} }
} }
func logMsg(m msg) { func logMsg(m msg) {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "messages", data: m} dataLogChan <- DataLogRow{tbl: "messages", data: m}
} }
} }
func logESMsg(m esmsg) { func logESMsg(m esmsg) {
if globalSettings.ReplayLog { if globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "es_messages", data: m} dataLogChan <- DataLogRow{tbl: "es_messages", data: m}
} }
} }
func logDump1090TermMessage(m Dump1090TermMessage) { func logDump1090TermMessage(m Dump1090TermMessage) {
if globalSettings.DEBUG && globalSettings.ReplayLog { if globalSettings.DEBUG && globalSettings.ReplayLog && dataLogReadyToWrite {
dataLogChan <- DataLogRow{tbl: "dump1090_terminal", data: m} dataLogChan <- DataLogRow{tbl: "dump1090_terminal", data: m}
} }
} }
func initDataLog() { func initDataLog() {
log.Printf("dataLogStarted = %t. dataLogReadyToWrite = %t\n", dataLogStarted, dataLogReadyToWrite) //REMOVE -- DEBUG
insertString = make(map[string]string) insertString = make(map[string]string)
insertBatchIfs = make(map[string][][]interface{}) insertBatchIfs = make(map[string][][]interface{})
go dataLog() if globalSettings.ReplayLog {
go dataLog()
}
go dataLogWatchdog()
log.Printf("initDataLog complete.\n") //REMOVE -- DEBUG
}
// Watchdog function to control startup / shutdown of data logging subsystem
func dataLogWatchdog() {
for {
log.Printf("Watchdog loop begins. dataLogStarted = %t\n", dataLogStarted)
if !dataLogStarted && globalSettings.ReplayLog { // case 1: sqlite logging isn't running, and we want to start it
log.Printf("Watchdog wants to START logging.\n")
go dataLog()
} else if dataLogStarted && !globalSettings.ReplayLog { // case 2: sqlite logging is running, and we want to shut it down
log.Printf("Watchdog wants to STOP logging.\n")
closeDataLog()
}
log.Printf("Watchdog iterated.\n") //REMOVE -- DEBUG
time.Sleep(1 * time.Second)
log.Printf("Watchdog sleep over.\n") //REMOVE -- DEBUG
}
}
// Handler for graceful shutdown of data logging goroutines. Intended to be called by dataLogWatchdog() and gracefulShutdown()
func closeDataLog() {
log.Printf("closeDataLog(): dataLogStarted = %t\n", dataLogStarted) //REMOVE -- DEBUG
dataLogReadyToWrite = false // prevent any new messages from being sent down the channels
log.Printf("Shutting down SQLite data log\n")
shutdownDataLogWriter <- true
log.Printf("Waiting for signal from dataLog()") //REMOVE -- DEBUG
for dataLogStarted {
log.Printf("closeDataLog(): dataLogStarted = %t\n", dataLogStarted) //REMOVE -- DEBUG
time.Sleep(50 * time.Millisecond)
}
log.Printf("closeDataLog(): Finished wait. dataLogStarted = %t\n", dataLogStarted) //REMOVE -- DEBUG
log.Printf("closeDataLog() complete")
} }

Wyświetl plik

@ -1191,8 +1191,14 @@ func gracefulShutdown() {
// Shut down SDRs. // Shut down SDRs.
sdrKill() sdrKill()
//TODO: Any other graceful shutdown functions. //TODO: Any other graceful shutdown functions.
// Shut down data logging. // Shut down data logging.
shutdownDataLog <- true if dataLogStarted {
closeDataLog()
//log.Printf("Waiting for log file to close\n")
//time.Sleep(3*time.Second) // FIXME
}
// shutdownDataLog <- true
os.Exit(1) os.Exit(1)
} }