transactions.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package sqlstore
  2. import (
  3. "context"
  4. "time"
  5. "github.com/grafana/grafana/pkg/bus"
  6. "github.com/grafana/grafana/pkg/log"
  7. sqlite3 "github.com/mattn/go-sqlite3"
  8. )
  9. func (ss *SqlStore) InTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
  10. return ss.inTransactionWithRetry(ctx, fn, 0)
  11. }
  12. func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
  13. sess, err := startSession(ctx, ss.engine, true)
  14. if err != nil {
  15. return err
  16. }
  17. defer sess.Close()
  18. withValue := context.WithValue(ctx, ContextSessionName, sess)
  19. err = fn(withValue)
  20. // special handling of database locked errors for sqlite, then we can retry 3 times
  21. if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
  22. if sqlError.Code == sqlite3.ErrLocked {
  23. sess.Rollback()
  24. time.Sleep(time.Millisecond * time.Duration(10))
  25. ss.log.Info("Database table locked, sleeping then retrying", "retry", retry)
  26. return ss.inTransactionWithRetry(ctx, fn, retry+1)
  27. }
  28. }
  29. if err != nil {
  30. sess.Rollback()
  31. return err
  32. }
  33. if err = sess.Commit(); err != nil {
  34. return err
  35. }
  36. if len(sess.events) > 0 {
  37. for _, e := range sess.events {
  38. if err = bus.Publish(e); err != nil {
  39. ss.log.Error("Failed to publish event after commit", err)
  40. }
  41. }
  42. }
  43. return nil
  44. }
  45. func inTransactionWithRetry(callback dbTransactionFunc, retry int) error {
  46. return inTransactionWithRetryCtx(context.Background(), callback, retry)
  47. }
  48. func inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
  49. sess, err := startSession(ctx, x, true)
  50. if err != nil {
  51. return err
  52. }
  53. defer sess.Close()
  54. err = callback(sess)
  55. // special handling of database locked errors for sqlite, then we can retry 3 times
  56. if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
  57. if sqlError.Code == sqlite3.ErrLocked {
  58. sess.Rollback()
  59. time.Sleep(time.Millisecond * time.Duration(10))
  60. sqlog.Info("Database table locked, sleeping then retrying", "retry", retry)
  61. return inTransactionWithRetry(callback, retry+1)
  62. }
  63. }
  64. if err != nil {
  65. sess.Rollback()
  66. return err
  67. } else if err = sess.Commit(); err != nil {
  68. return err
  69. }
  70. if len(sess.events) > 0 {
  71. for _, e := range sess.events {
  72. if err = bus.Publish(e); err != nil {
  73. log.Error(3, "Failed to publish event after commit", err)
  74. }
  75. }
  76. }
  77. return nil
  78. }
  79. func inTransaction(callback dbTransactionFunc) error {
  80. return inTransactionWithRetry(callback, 0)
  81. }
  82. func inTransactionCtx(ctx context.Context, callback dbTransactionFunc) error {
  83. return inTransactionWithRetryCtx(ctx, callback, 0)
  84. }