-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgarbage-collector.go
126 lines (108 loc) · 4.42 KB
/
garbage-collector.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Garbage Collector.
package main
import (
_ "github.com/go-sql-driver/mysql"
"database/sql"
"strings"
"time"
)
// garbageCollector goes through the database every few hours or so, pings the objects and sees if they're alive, checks their timestamps, and if they are too old, remove them from the database.
func garbageCollector() {
Log.Info("Garbage Collector called.")
// use a ticker
ticker := time.NewTicker(time.Hour * 4)
//ticker := time.NewTicker(time.Minute * 1)
go func() {
var agent AgentType
var position PositionType
var toDeleteUUIDs []string // collect list of UUIDs to delete
var callResult string
for t := range ticker.C {
Log.Debug("\n\n\n\n\nrunning at", t)
// see which Agents are dead
db, err := sql.Open(PDO_Prefix, GoBotDSN)
checkErr(err)
rows, err := db.Query("SELECT `UUID`, `PermURL` FROM `Agents`")
checkErr(err)
toDeleteUUIDs = nil // reset our array
for rows.Next() {
err = rows.Scan(
&agent.UUID,
&agent.PermURL,
)
checkErr(err) // cannot hurt
// TODO(gwyneth): test 2 or 3 times, sometimes we exaust HTTP requests. Also, deal with a way for the LSL scripts
// attempt to register after an hour or so instead of entering BROKEN mode (20170806).
// now call the ping on this Agent
callResult, err = callURL(*agent.PermURL.Ptr(), "command=ping")
Log.Debug("Agent", *agent.UUID.Ptr(), "Result of calling", *agent.PermURL.Ptr(), ":", callResult, "Error:", err)
if err != nil || callResult != "pong" {
// either dead, zombie, or misbehaving, kill this agent by placing it in the list
toDeleteUUIDs = append(toDeleteUUIDs, "'" + *agent.UUID.Ptr() + "'")
}
}
rows.Close()
if len(toDeleteUUIDs) > 0 {
killAgents := strings.Join(toDeleteUUIDs, ",")
Log.Warning("The following agents did not reply to the ping command: ", killAgents)
result, err := db.Exec("DELETE FROM `Agents` WHERE `UUID` IN (" + killAgents + ")")
checkErr(err)
rowsAffected, err := result.RowsAffected()
if err == nil {
Log.Info("deleted", rowsAffected, "zombie agents with UUIDs: ", killAgents) // probably not needed
} else {
Log.Warning("deleted an unknown number of zombie agents with UUIDs: ", killAgents)
}
} else {
Log.Debug("no agents to delete")
}
// see which Cubes (positions) are dead
rows, err = db.Query("SELECT `UUID`, `PermURL` FROM `Positions`")
checkErr(err)
toDeleteUUIDs = nil // reset our array again
for rows.Next() {
err = rows.Scan(
&position.UUID,
&position.PermURL,
)
checkErr(err)
// now call the ping on this cube
callResult, err = callURL(*position.PermURL.Ptr(), "command=ping")
Log.Debug("Cube", *position.UUID.Ptr(), "Result of calling", *position.PermURL.Ptr(), ":", callResult, "Error:", err)
if err != nil || callResult != "pong" {
// either dead, zombie, or misbehaving, kill this cube by placing it in the list
toDeleteUUIDs = append(toDeleteUUIDs, "'" + *position.UUID.Ptr() + "'")
}
}
rows.Close()
if len(toDeleteUUIDs) > 0 {
killPositions := strings.Join(toDeleteUUIDs, ",")
result, err := db.Exec("DELETE FROM `Positions` WHERE `UUID` IN (" + killPositions + ")")
checkErr(err)
rowsAffected, err := result.RowsAffected()
if err == nil {
Log.Info("deleted", rowsAffected, "zombie cubes with UUIDs: ", killPositions)
} else {
Log.Warning("deleted an unknown number of cubes with UUIDs: ", killPositions)
}
} else {
Log.Debug("no cubes to delete")
}
// see which Objects (positions) are dead
// This is trickier, because objects are passive!
// The best we can do is simply to delete old entries — 4 hours is an arbitrary number — and expect that agents will
// start checking in again new entries
// TODO(gwyneth): maybe cubes ought also run the Sensorama.lsl script to aid in detecting (and refreshing) 'permanent' objects?
// Actually, that's a pretty good idea :-) (20170731)
result, err := db.Exec("DELETE FROM `Obstacles` WHERE `LastUpdate` < ADDDATE(NOW(), INTERVAL -4 HOUR)")
checkErr(err)
rowsAffected, err := result.RowsAffected()
if err == nil {
Log.Info("deleted", rowsAffected, "obstacles which haven't been seen in the past 4 hours")
} else {
Log.Warning("Couldn't delete any obstacles; reason:", err)
}
db.Close()
}
}()
}