Explorar o código

expose functions to use sessions

bergquist %!s(int64=7) %!d(string=hai) anos
pai
achega
3479cf4b39
Modificáronse 2 ficheiros con 69 adicións e 11 borrados
  1. 10 10
      pkg/services/sqlstore/session.go
  2. 59 1
      pkg/services/sqlstore/sqlstore.go

+ 10 - 10
pkg/services/sqlstore/session.go

@@ -27,18 +27,18 @@ func startSession(ctx context.Context, engine *xorm.Engine, beginTran bool) (*DB
 	var sess *DBSession
 	sess, ok := value.(*DBSession)
 
-	if !ok {
-		newSess := &DBSession{Session: engine.NewSession()}
-		if beginTran {
-			err := newSess.Begin()
-			if err != nil {
-				return nil, err
-			}
-		}
-		return newSess, nil
+	if ok {
+		return sess, nil
 	}
 
-	return sess, nil
+	newSess := &DBSession{Session: engine.NewSession()}
+	if beginTran {
+		err := newSess.Begin()
+		if err != nil {
+			return nil, err
+		}
+	}
+	return newSess, nil
 }
 
 func withDbSession(ctx context.Context, callback dbTransactionFunc) error {

+ 59 - 1
pkg/services/sqlstore/sqlstore.go

@@ -26,7 +26,7 @@ import (
 
 	_ "github.com/grafana/grafana/pkg/tsdb/mssql"
 	_ "github.com/lib/pq"
-	_ "github.com/mattn/go-sqlite3"
+	sqlite3 "github.com/mattn/go-sqlite3"
 )
 
 var (
@@ -56,6 +56,64 @@ type SqlStore struct {
 	skipEnsureAdmin bool
 }
 
+// NewSession returns a new DBSession
+func (ss *SqlStore) NewSession() *DBSession {
+	return &DBSession{Session: ss.engine.NewSession()}
+}
+
+// WithDbSession calls the callback with an session attached to the context.
+func (ss *SqlStore) WithDbSession(ctx context.Context, callback dbTransactionFunc) error {
+	sess, err := startSession(ctx, ss.engine, false)
+	if err != nil {
+		return err
+	}
+
+	return callback(sess)
+}
+
+// WithTransactionalDbSession calls the callback with an session within a transaction
+func (ss *SqlStore) WithTransactionalDbSession(ctx context.Context, callback dbTransactionFunc) error {
+	return ss.inTransactionWithRetryCtx(ctx, callback, 0)
+}
+
+func (ss *SqlStore) inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
+	sess, err := startSession(ctx, ss.engine, true)
+	if err != nil {
+		return err
+	}
+
+	defer sess.Close()
+
+	err = callback(sess)
+
+	// special handling of database locked errors for sqlite, then we can retry 3 times
+	if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
+		if sqlError.Code == sqlite3.ErrLocked {
+			sess.Rollback()
+			time.Sleep(time.Millisecond * time.Duration(10))
+			sqlog.Info("Database table locked, sleeping then retrying", "retry", retry)
+			return ss.inTransactionWithRetryCtx(ctx, callback, retry+1)
+		}
+	}
+
+	if err != nil {
+		sess.Rollback()
+		return err
+	} else if err = sess.Commit(); err != nil {
+		return err
+	}
+
+	if len(sess.events) > 0 {
+		for _, e := range sess.events {
+			if err = bus.Publish(e); err != nil {
+				log.Error(3, "Failed to publish event after commit", err)
+			}
+		}
+	}
+
+	return nil
+}
+
 func (ss *SqlStore) Init() error {
 	ss.log = log.New("sqlstore")
 	ss.readConfig()