Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically pause queue if index service is unavailable #15066

Merged
merged 9 commits into from
Jan 27, 2022

Conversation

lafriks
Copy link
Member

@lafriks lafriks commented Mar 20, 2021

Fixes that changes are not indexed and lost if external index services is not available

If indexer service is unavailable for search, display error:
image

If code search service is unavailable, display error:
attels

attels

Depends on #15928

@lafriks lafriks changed the title Make queue pausable if issue index service is unavailable WIP: Make queue pausable if issue index service is unavailable Mar 20, 2021
@lafriks lafriks added type/bug pr/wip This PR is not ready for review labels Mar 20, 2021
@lafriks lafriks mentioned this pull request Jul 14, 2021
@stale
Copy link

stale bot commented Jul 21, 2021

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you for your contributions.

@stale stale bot added the issue/stale label Jul 21, 2021
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Jul 21, 2021
@stale stale bot removed the issue/stale label Jul 21, 2021
@lafriks lafriks force-pushed the feat/indexer_availability branch 2 times, most recently from 9aa2ac2 to ac21844 Compare January 21, 2022 13:45
@lafriks lafriks changed the title WIP: Make queue pausable if issue index service is unavailable WIP: Automatically pause queue if index service is unavailable Jan 21, 2022
@lafriks lafriks added this to the 1.17.0 milestone Jan 21, 2022
@lafriks lafriks force-pushed the feat/indexer_availability branch 2 times, most recently from 62d56b0 to c9b8da4 Compare January 24, 2022 15:00
@lafriks lafriks changed the title WIP: Automatically pause queue if index service is unavailable Automatically pause queue if index service is unavailable Jan 24, 2022
@lafriks lafriks removed the pr/wip This PR is not ready for review label Jan 24, 2022
@lafriks
Copy link
Member Author

lafriks commented Jan 24, 2022

Ready for review

@GiteaBot GiteaBot added lgtm/need 1 This PR needs approval from one additional maintainer to be merged. and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Jan 25, 2022
modules/indexer/code/elastic_search.go Outdated Show resolved Hide resolved
modules/indexer/code/elastic_search.go Outdated Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Outdated Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Outdated Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Outdated Show resolved Hide resolved
modules/indexer/issues/elastic_search.go Outdated Show resolved Hide resolved
Copy link
Contributor

@zeripath zeripath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately there are a few races here. RCing to prevent merging.

@zeripath
Copy link
Contributor

Hi @lafriks

Concurrency Fixing Patch
diff --git a/modules/indexer/code/elastic_search.go b/modules/indexer/code/elastic_search.go
index c1d4c29b8..a8b171194 100644
--- a/modules/indexer/code/elastic_search.go
+++ b/modules/indexer/code/elastic_search.go
@@ -13,12 +13,14 @@ import (
 	"net"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/analyze"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -46,6 +48,7 @@ type ElasticSearchIndexer struct {
 	available            bool
 	availabilityCallback func(bool)
 	stopTimer            chan struct{}
+	lock                 sync.Mutex
 }
 
 type elasticLogger struct {
@@ -198,11 +201,15 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
 
 // SetAvailabilityChangeCallback sets callback that will be triggered when availability changes
 func (b *ElasticSearchIndexer) SetAvailabilityChangeCallback(callback func(bool)) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
 	b.availabilityCallback = callback
 }
 
 // Ping checks if elastic is available
 func (b *ElasticSearchIndexer) Ping() bool {
+	b.lock.Lock()
+	defer b.lock.Unlock()
 	return b.available
 }
 
@@ -486,27 +493,41 @@ func (b *ElasticSearchIndexer) Close() {
 
 func (b *ElasticSearchIndexer) checkError(err error) error {
 	var opErr *net.OpError
-	if b.available && (elastic.IsConnErr(err) || (errors.As(err, &opErr) && (opErr.Op == "dial" || opErr.Op == "read"))) {
-		b.available = false
-		if b.availabilityCallback != nil {
-			b.availabilityCallback(b.available)
-		}
+	if !(elastic.IsConnErr(err) || (errors.As(err, &opErr) && (opErr.Op == "dial" || opErr.Op == "read"))) {
+		return err
 	}
+
+	b.setAvailability(false)
+
 	return err
 }
 
 func (b *ElasticSearchIndexer) checkAvailability() {
-	if b.available {
+	if b.Ping() {
 		return
 	}
 
 	// Request cluster state to check if elastic is available again
-	_, err := b.client.ClusterState().Do(context.Background())
+	_, err := b.client.ClusterState().Do(graceful.GetManager().ShutdownContext())
 	if err != nil {
+		b.setAvailability(false)
+		return
+	}
+
+	b.setAvailability(true)
+}
+
+func (b *ElasticSearchIndexer) setAvailability(available bool) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
+
+	if b.available == available {
 		return
 	}
-	b.available = true
+
+	b.available = available
 	if b.availabilityCallback != nil {
+		// Call the callback from within the lock to ensure that the ordering remains correct
 		b.availabilityCallback(b.available)
 	}
 }
diff --git a/modules/indexer/issues/elastic_search.go b/modules/indexer/issues/elastic_search.go
index 6ad061802..80f69d3b1 100644
--- a/modules/indexer/issues/elastic_search.go
+++ b/modules/indexer/issues/elastic_search.go
@@ -10,8 +10,10 @@ import (
 	"fmt"
 	"net"
 	"strconv"
+	"sync"
 	"time"
 
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 
 	"github.com/olivere/elastic/v7"
@@ -26,6 +28,7 @@ type ElasticSearchIndexer struct {
 	available            bool
 	availabilityCallback func(bool)
 	stopTimer            chan struct{}
+	lock                 sync.Mutex
 }
 
 type elasticLogger struct {
@@ -138,11 +141,15 @@ func (b *ElasticSearchIndexer) Init() (bool, error) {
 
 // SetAvailabilityChangeCallback sets callback that will be triggered when availability changes
 func (b *ElasticSearchIndexer) SetAvailabilityChangeCallback(callback func(bool)) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
 	b.availabilityCallback = callback
 }
 
 // Ping checks if elastic is available
 func (b *ElasticSearchIndexer) Ping() bool {
+	b.lock.Lock()
+	defer b.lock.Unlock()
 	return b.available
 }
 
@@ -262,27 +269,41 @@ func (b *ElasticSearchIndexer) Close() {
 
 func (b *ElasticSearchIndexer) checkError(err error) error {
 	var opErr *net.OpError
-	if b.available && (elastic.IsConnErr(err) || (errors.As(err, &opErr) && (opErr.Op == "dial" || opErr.Op == "read"))) {
-		b.available = false
-		if b.availabilityCallback != nil {
-			b.availabilityCallback(b.available)
-		}
+	if !(elastic.IsConnErr(err) || (errors.As(err, &opErr) && (opErr.Op == "dial" || opErr.Op == "read"))) {
+		return err
 	}
+
+	b.setAvailability(false)
+
 	return err
 }
 
 func (b *ElasticSearchIndexer) checkAvailability() {
-	if b.available {
+	if b.Ping() {
 		return
 	}
 
 	// Request cluster state to check if elastic is available again
-	_, err := b.client.ClusterState().Do(context.Background())
+	_, err := b.client.ClusterState().Do(graceful.GetManager().ShutdownContext())
 	if err != nil {
+		b.setAvailability(false)
+		return
+	}
+
+	b.setAvailability(true)
+}
+
+func (b *ElasticSearchIndexer) setAvailability(available bool) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
+
+	if b.available == available {
 		return
 	}
-	b.available = true
+
+	b.available = available
 	if b.availabilityCallback != nil {
+		// Call the callback from within the lock to ensure that the ordering remains correct
 		b.availabilityCallback(b.available)
 	}
 }

@lafriks lafriks requested a review from zeripath January 25, 2022 20:18
@lafriks
Copy link
Member Author

lafriks commented Jan 25, 2022

Thanks for patch, updated to use RWMutex as Ping could be called quite often and it needs only RLock

@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/need 1 This PR needs approval from one additional maintainer to be merged. labels Jan 26, 2022
@lafriks
Copy link
Member Author

lafriks commented Jan 27, 2022

🚀

@lafriks lafriks merged commit 8038610 into go-gitea:main Jan 27, 2022
@lafriks lafriks deleted the feat/indexer_availability branch January 27, 2022 08:30
@Gusted Gusted mentioned this pull request Jan 28, 2022
zjjhot added a commit to zjjhot/gitea that referenced this pull request Jan 28, 2022
* 'main' of https://github.com/go-gitea/gitea:
  Allow to filter repositories by language in explore, user and organization repositories lists (go-gitea#18430)
  Fix broken when no commits and default branch is not master (go-gitea#18422)
  [skip ci] Updated translations via Crowdin
  Automatically pause queue if index service is unavailable (go-gitea#15066)
Chianina pushed a commit to Chianina/gitea that referenced this pull request Mar 28, 2022
…5066)

* Handle keyword search error when issue indexer service is not available

* Implement automatic disabling and resume of code indexer queue
@go-gitea go-gitea locked and limited conversation to collaborators Apr 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. type/bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants