From f062005946a930636b9b97b361370560cfeb9907 Mon Sep 17 00:00:00 2001 From: McMatts Date: Fri, 8 Mar 2019 15:50:55 +0000 Subject: [PATCH] Introduce transaction isolation customization Search related indexing transactions requires better TX begin/commit. New helpers provide TX isolation control. --- core/env/runtime.go | 59 +++++++++++++++++++--------------------- domain/search/search.go | 56 +++++++++++++++++++++----------------- domain/search/store.go | 6 ---- domain/space/endpoint.go | 6 ++-- domain/store/storer.go | 1 + 5 files changed, 63 insertions(+), 65 deletions(-) diff --git a/core/env/runtime.go b/core/env/runtime.go index 1dc73f8d..1b0fb688 100644 --- a/core/env/runtime.go +++ b/core/env/runtime.go @@ -13,25 +13,25 @@ package env import ( - "context" - "database/sql" + "context" + "database/sql" - "github.com/documize/community/domain" + "github.com/documize/community/domain" "github.com/jmoiron/sqlx" ) const ( - // SiteModeNormal serves app - SiteModeNormal = "" + // SiteModeNormal serves app + SiteModeNormal = "" - // SiteModeOffline serves offline.html - SiteModeOffline = "1" + // SiteModeOffline serves offline.html + SiteModeOffline = "1" - // SiteModeSetup tells Ember to serve setup route - SiteModeSetup = "2" + // SiteModeSetup tells Ember to serve setup route + SiteModeSetup = "2" - // SiteModeBadDB redirects to db-error.html page - SiteModeBadDB = "3" + // SiteModeBadDB redirects to db-error.html page + SiteModeBadDB = "3" ) // Runtime provides access to database, logger and other server-level scoped objects. @@ -44,40 +44,37 @@ type Runtime struct { Product domain.Product } -var ctx = context.Background() - // StartTx beings database transaction with application defined // database transaction isolation level. // Any error encountered during this operation is logged to runtime logger. -func (r *Runtime) StartTx() (tx *sqlx.Tx, ok bool) { - tx, err := r.Db.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) - if err != nil { - r.Log.Error("unable to start database transaction", err) - return nil, false - } +func (r *Runtime) StartTx(i sql.IsolationLevel) (tx *sqlx.Tx, ok bool) { + tx, err := r.Db.BeginTxx(context.Background(), &sql.TxOptions{Isolation: i}) + if err != nil { + r.Log.Error("unable to start database transaction", err) + return nil, false + } - return tx, true + return tx, true } // Rollback aborts active database transaction. // Any error encountered during this operation is logged to runtime logger. func (r *Runtime) Rollback(tx *sqlx.Tx) bool { - if err := tx.Commit(); err != nil { - r.Log.Error("unable to commit database transaction", err) - return false - } + if err := tx.Commit(); err != nil { + r.Log.Error("unable to commit database transaction", err) + return false + } - return true + return true } // Commit flushes pending changes to database. // Any error encountered during this operation is logged to runtime logger. func (r *Runtime) Commit(tx *sqlx.Tx) bool { - if err := tx.Commit(); err != nil { - r.Log.Error("unable to commit database transaction", err) - return false - } + if err := tx.Commit(); err != nil { + r.Log.Error("unable to commit database transaction", err) + return false + } - return true + return true } - diff --git a/domain/search/search.go b/domain/search/search.go index 56088550..d2170e14 100644 --- a/domain/search/search.go +++ b/domain/search/search.go @@ -12,12 +12,14 @@ package search import ( + "database/sql" "github.com/documize/community/domain" "github.com/documize/community/model/attachment" "github.com/documize/community/model/category" "github.com/documize/community/model/doc" "github.com/documize/community/model/page" sm "github.com/documize/community/model/search" + "github.com/documize/community/model/workflow" ) // IndexDocument adds search indesd entries for document inserting title, tags and attachments as @@ -26,20 +28,21 @@ func (m *Indexer) IndexDocument(ctx domain.RequestContext, d doc.Document, a []a method := "search.IndexDocument" var err error - ctx.Transaction, err = m.runtime.Db.Beginx() - if err != nil { - m.runtime.Log.Error(method, err) + ok := true + ctx.Transaction, ok = m.runtime.StartTx(sql.LevelReadUncommitted) + if !ok { + m.runtime.Log.Info("unable to start TX for " + method) return } err = m.store.Search.IndexDocument(ctx, d, a) if err != nil { - ctx.Transaction.Rollback() + m.runtime.Rollback(ctx.Transaction) m.runtime.Log.Error(method, err) return } - ctx.Transaction.Commit() + m.runtime.Commit(ctx.Transaction) } // DeleteDocument removes all search entries for document. @@ -47,20 +50,21 @@ func (m *Indexer) DeleteDocument(ctx domain.RequestContext, ID string) { method := "search.DeleteDocument" var err error - ctx.Transaction, err = m.runtime.Db.Beginx() - if err != nil { - m.runtime.Log.Error(method, err) + ok := true + ctx.Transaction, ok = m.runtime.StartTx(sql.LevelReadUncommitted) + if !ok { + m.runtime.Log.Info("unable to start TX for " + method) return } err = m.store.Search.DeleteDocument(ctx, ID) if err != nil { - ctx.Transaction.Rollback() + m.runtime.Rollback(ctx.Transaction) m.runtime.Log.Error(method, err) return } - ctx.Transaction.Commit() + m.runtime.Commit(ctx.Transaction) } // IndexContent adds search index entry for document context. @@ -69,25 +73,26 @@ func (m *Indexer) IndexContent(ctx domain.RequestContext, p page.Page) { method := "search.IndexContent" var err error - ctx.Transaction, err = m.runtime.Db.Beginx() - if err != nil { - m.runtime.Log.Error(method, err) + // we do not index pending pages + if p.Status == workflow.ChangePending || p.Status == workflow.ChangePendingNew { + return + } + + ok := true + ctx.Transaction, ok = m.runtime.StartTx(sql.LevelReadUncommitted) + if !ok { + m.runtime.Log.Info("unable to start TX for " + method) return } err = m.store.Search.IndexContent(ctx, p) if err != nil { - ctx.Transaction.Rollback() + m.runtime.Rollback(ctx.Transaction) m.runtime.Log.Error(method, err) return } - err = ctx.Transaction.Commit() - if err != nil { - ctx.Transaction.Rollback() - m.runtime.Log.Error(method, err) - return - } + m.runtime.Commit(ctx.Transaction) } // DeleteContent removes all search entries for specific document content. @@ -95,20 +100,21 @@ func (m *Indexer) DeleteContent(ctx domain.RequestContext, pageID string) { method := "search.DeleteContent" var err error - ctx.Transaction, err = m.runtime.Db.Beginx() - if err != nil { - m.runtime.Log.Error(method, err) + ok := true + ctx.Transaction, ok = m.runtime.StartTx(sql.LevelReadUncommitted) + if !ok { + m.runtime.Log.Info("unable to start TX for " + method) return } err = m.store.Search.DeleteContent(ctx, pageID) if err != nil { - ctx.Transaction.Rollback() + m.runtime.Rollback(ctx.Transaction) m.runtime.Log.Error(method, err) return } - ctx.Transaction.Commit() + m.runtime.Commit(ctx.Transaction) } // FilterCategoryProtected removes search results that cannot be seen by user diff --git a/domain/search/store.go b/domain/search/store.go index 1520575d..cae75ca2 100644 --- a/domain/search/store.go +++ b/domain/search/store.go @@ -24,7 +24,6 @@ import ( "github.com/documize/community/model/doc" "github.com/documize/community/model/page" "github.com/documize/community/model/search" - "github.com/documize/community/model/workflow" "github.com/pkg/errors" ) @@ -125,11 +124,6 @@ func (s Store) DeleteDocument(ctx domain.RequestContext, ID string) (err error) func (s Store) IndexContent(ctx domain.RequestContext, p page.Page) (err error) { method := "search.IndexContent" - // we do not index pending pages - if p.Status == workflow.ChangePending || p.Status == workflow.ChangePendingNew { - return - } - // remove previous search entries _, err = ctx.Transaction.Exec(s.Bind("DELETE FROM dmz_search WHERE c_orgid=? AND c_docid=? AND c_itemid=? AND c_itemtype='page'"), ctx.OrgID, p.DocumentID, p.RefID) diff --git a/domain/space/endpoint.go b/domain/space/endpoint.go index 0d87bdf4..9bf16296 100644 --- a/domain/space/endpoint.go +++ b/domain/space/endpoint.go @@ -696,7 +696,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { // Delete the space first. ok := true - ctx.Transaction, ok = h.Runtime.StartTx() + ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted) if !ok { response.WriteError(w, method) return @@ -712,7 +712,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { h.Runtime.Commit(ctx.Transaction) // Delete data associated with this space. - ctx.Transaction, ok = h.Runtime.StartTx() + ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted) if !ok { response.WriteError(w, method) return @@ -756,7 +756,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) { h.Runtime.Commit(ctx.Transaction) // Record this action. - ctx.Transaction, ok = h.Runtime.StartTx() + ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted) if !ok { response.WriteError(w, method) return diff --git a/domain/store/storer.go b/domain/store/storer.go index 8ffdb12f..ef9cf9a6 100644 --- a/domain/store/storer.go +++ b/domain/store/storer.go @@ -184,6 +184,7 @@ type DocumentStorer interface { TemplatesBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) PublicDocuments(ctx domain.RequestContext, orgID string) (documents []doc.SitemapDocument, err error) Update(ctx domain.RequestContext, document doc.Document) (err error) + UpdateRevised(ctx domain.RequestContext, docID string) (err error) UpdateGroup(ctx domain.RequestContext, document doc.Document) (err error) ChangeDocumentSpace(ctx domain.RequestContext, document, space string) (err error) MoveDocumentSpace(ctx domain.RequestContext, id, move string) (err error)