garbage-collector.go 4.4 KB

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