shared.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. package sqlstore
  2. import (
  3. "time"
  4. "github.com/go-xorm/xorm"
  5. "github.com/grafana/grafana/pkg/bus"
  6. "github.com/grafana/grafana/pkg/log"
  7. sqlite3 "github.com/mattn/go-sqlite3"
  8. )
  9. type DBSession struct {
  10. *xorm.Session
  11. events []interface{}
  12. }
  13. type dbTransactionFunc func(sess *DBSession) error
  14. func (sess *DBSession) publishAfterCommit(msg interface{}) {
  15. sess.events = append(sess.events, msg)
  16. }
  17. func newSession() *DBSession {
  18. return &DBSession{Session: x.NewSession()}
  19. }
  20. func inTransaction(callback dbTransactionFunc) error {
  21. return inTransactionWithRetry(callback, 0)
  22. }
  23. func inTransactionWithRetry(callback dbTransactionFunc, retry int) error {
  24. var err error
  25. sess := newSession()
  26. defer sess.Close()
  27. if err = sess.Begin(); err != nil {
  28. return err
  29. }
  30. err = callback(sess)
  31. // special handling of database locked errors for sqlite, then we can retry 3 times
  32. if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
  33. if sqlError.Code == sqlite3.ErrLocked {
  34. sess.Rollback()
  35. time.Sleep(time.Millisecond * time.Duration(10))
  36. sqlog.Info("Database table locked, sleeping then retrying", "retry", retry)
  37. return inTransactionWithRetry(callback, retry+1)
  38. }
  39. }
  40. if err != nil {
  41. sess.Rollback()
  42. return err
  43. } else if err = sess.Commit(); err != nil {
  44. return err
  45. }
  46. if len(sess.events) > 0 {
  47. for _, e := range sess.events {
  48. if err = bus.Publish(e); err != nil {
  49. log.Error(3, "Failed to publish event after commit", err)
  50. }
  51. }
  52. }
  53. return nil
  54. }