commit.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package database
  2. import (
  3. "time"
  4. "github.com/crowdsecurity/crowdsec/pkg/types"
  5. "github.com/pkg/errors"
  6. log "github.com/sirupsen/logrus"
  7. )
  8. func (c *Context) DeleteExpired() error {
  9. c.lock.Lock()
  10. defer c.lock.Unlock()
  11. //Delete the expired records
  12. now := time.Now()
  13. if c.flush {
  14. retx := c.Db.Delete(types.BanApplication{}, "until < ?", now)
  15. if retx.RowsAffected > 0 {
  16. log.Infof("Flushed %d expired entries from Ban Application", retx.RowsAffected)
  17. }
  18. }
  19. return nil
  20. }
  21. /*Flush doesn't do anything here : we are not using transactions or such, nothing to "flush" per se*/
  22. func (c *Context) Flush() error {
  23. return nil
  24. }
  25. func (c *Context) CleanUpRecordsByAge() error {
  26. //let's fetch all expired records that are more than XX days olds
  27. sos := []types.BanApplication{}
  28. if c.maxDurationRetention == 0 {
  29. return nil
  30. }
  31. //look for soft-deleted events that are OLDER than maxDurationRetention
  32. ret := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").
  33. Where("deleted_at < ?", time.Now().Add(-c.maxDurationRetention)).
  34. Order("updated_at desc").Find(&sos)
  35. if ret.Error != nil {
  36. return errors.Wrap(ret.Error, "failed to get count of old records")
  37. }
  38. //no events elligible
  39. if len(sos) == 0 || ret.RowsAffected == 0 {
  40. log.Debugf("no event older than %s", c.maxDurationRetention.String())
  41. return nil
  42. }
  43. delRecords := 0
  44. for _, record := range sos {
  45. copy := record
  46. if ret := c.Db.Unscoped().Table("signal_occurences").Where("ID = ?", copy.SignalOccurenceID).Delete(&types.SignalOccurence{}); ret.Error != nil {
  47. return errors.Wrap(ret.Error, "failed to clean signal_occurences")
  48. }
  49. if ret := c.Db.Unscoped().Table("event_sequences").Where("signal_occurence_id = ?", copy.SignalOccurenceID).Delete(&types.EventSequence{}); ret.Error != nil {
  50. return errors.Wrap(ret.Error, "failed to clean event_sequences")
  51. }
  52. if ret := c.Db.Unscoped().Table("ban_applications").Delete(&copy); ret.Error != nil {
  53. return errors.Wrap(ret.Error, "failed to clean ban_applications")
  54. }
  55. delRecords++
  56. }
  57. log.Printf("max_records_age: deleting %d events (max age:%s)", delRecords, c.maxDurationRetention)
  58. return nil
  59. }
  60. func (c *Context) CleanUpRecordsByCount() error {
  61. var count int
  62. if c.maxEventRetention <= 0 {
  63. return nil
  64. }
  65. ret := c.Db.Unscoped().Table("ban_applications").Order("updated_at desc").Count(&count)
  66. if ret.Error != nil {
  67. return errors.Wrap(ret.Error, "failed to get bans count")
  68. }
  69. if count < c.maxEventRetention {
  70. log.Debugf("%d < %d, don't cleanup", count, c.maxEventRetention)
  71. return nil
  72. }
  73. sos := []types.BanApplication{}
  74. now := time.Now()
  75. /*get soft deleted records oldest to youngest*/
  76. //records := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").Where(`strftime("%s", deleted_at) < strftime("%s", "now")`).Find(&sos)
  77. records := c.Db.Unscoped().Table("ban_applications").Where("deleted_at is not NULL").Where("deleted_at < ?", now).Find(&sos)
  78. if records.Error != nil {
  79. return errors.Wrap(records.Error, "failed to list expired bans for flush")
  80. }
  81. //let's do it in a single transaction
  82. delRecords := 0
  83. for _, ld := range sos {
  84. copy := ld
  85. if ret := c.Db.Unscoped().Table("signal_occurences").Where("ID = ?", copy.SignalOccurenceID).Delete(&types.SignalOccurence{}); ret.Error != nil {
  86. return errors.Wrap(ret.Error, "failed to clean signal_occurences")
  87. }
  88. if ret := c.Db.Unscoped().Table("event_sequences").Where("signal_occurence_id = ?", copy.SignalOccurenceID).Delete(&types.EventSequence{}); ret.Error != nil {
  89. return errors.Wrap(ret.Error, "failed to clean event_sequences")
  90. }
  91. if ret := c.Db.Unscoped().Table("ban_applications").Delete(&copy); ret.Error != nil {
  92. return errors.Wrap(ret.Error, "failed to clean ban_applications")
  93. }
  94. //we need to delete associations : event_sequences, signal_occurences
  95. delRecords++
  96. //let's delete as well the associated event_sequence
  97. if count-delRecords <= c.maxEventRetention {
  98. break
  99. }
  100. }
  101. if len(sos) > 0 {
  102. log.Printf("max_records: deleting %d events. (%d soft-deleted)", delRecords, len(sos))
  103. } else {
  104. log.Debugf("didn't find any record to clean")
  105. }
  106. return nil
  107. }
  108. func (c *Context) StartAutoCommit() error {
  109. //TBD : we shouldn't start auto-commit if we are in cli mode ?
  110. c.PusherTomb.Go(func() error {
  111. c.autoCommit()
  112. return nil
  113. })
  114. return nil
  115. }
  116. func (c *Context) autoCommit() {
  117. log.Debugf("starting autocommit")
  118. cleanUpTicker := time.NewTicker(1 * time.Minute)
  119. expireTicker := time.NewTicker(1 * time.Second)
  120. if !c.flush {
  121. log.Debugf("flush is disabled")
  122. }
  123. for {
  124. select {
  125. case <-c.PusherTomb.Dying():
  126. //we need to shutdown
  127. log.Infof("database routine shutdown")
  128. if err := c.Flush(); err != nil {
  129. log.Errorf("error while flushing records: %s", err)
  130. }
  131. if err := c.Db.Close(); err != nil {
  132. log.Errorf("error while closing db : %s", err)
  133. }
  134. return
  135. case <-expireTicker.C:
  136. if err := c.DeleteExpired(); err != nil {
  137. log.Errorf("Error while deleting expired records: %s", err)
  138. }
  139. case <-cleanUpTicker.C:
  140. if err := c.CleanUpRecordsByCount(); err != nil {
  141. log.Errorf("error in max records cleanup : %s", err)
  142. }
  143. if err := c.CleanUpRecordsByAge(); err != nil {
  144. log.Errorf("error in old records cleanup : %s", err)
  145. }
  146. }
  147. }
  148. }