Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2021-07-08 16:54:32 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2021-09-01 14:52:33 +0300
commit4df1f179d4cf1e60de941fc5d513fa496f666d52 (patch)
treeb59c83fdcd8c0dc85711470ccb143908f4b9c681
parent74ac90be1fa25803fe2020b3afe4a4f85e2731ac (diff)
transactions: Implement function which tells whether votes did vote
It's currently not possible to tell whether a given node has cast any vote or not. Implement it -- we'll need this as a heuristic to determine whether the primary has been dirtied. (cherry picked from commit 56b43c97077bbf6084ace172b22c9d7b70b9f91f)
-rw-r--r--internal/praefect/coordinator_test.go5
-rw-r--r--internal/praefect/transactions/subtransaction.go18
-rw-r--r--internal/praefect/transactions/transaction.go24
-rw-r--r--internal/praefect/transactions/transaction_test.go27
4 files changed, 74 insertions, 0 deletions
diff --git a/internal/praefect/coordinator_test.go b/internal/praefect/coordinator_test.go
index ddd32bf63..8ba453211 100644
--- a/internal/praefect/coordinator_test.go
+++ b/internal/praefect/coordinator_test.go
@@ -1509,6 +1509,7 @@ type mockTransaction struct {
nodeStates map[string]transactions.VoteResult
subtransactions int
didCommitAnySubtransaction bool
+ didVote map[string]bool
}
func (t mockTransaction) ID() uint64 {
@@ -1523,6 +1524,10 @@ func (t mockTransaction) DidCommitAnySubtransaction() bool {
return t.didCommitAnySubtransaction
}
+func (t mockTransaction) DidVote(node string) bool {
+ return t.didVote[node]
+}
+
func (t mockTransaction) State() (map[string]transactions.VoteResult, error) {
return t.nodeStates, nil
}
diff --git a/internal/praefect/transactions/subtransaction.go b/internal/praefect/transactions/subtransaction.go
index 7636a43eb..87b0ae1b0 100644
--- a/internal/praefect/transactions/subtransaction.go
+++ b/internal/praefect/transactions/subtransaction.go
@@ -345,3 +345,21 @@ func (t *subtransaction) getResult(node string) (VoteResult, error) {
return voter.result, nil
}
+
+func (t *subtransaction) getVote(node string) (*voting.Vote, error) {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ voter, ok := t.votersByNode[node]
+ if !ok {
+ return nil, fmt.Errorf("invalid node for transaction: %q", node)
+ }
+
+ if voter.vote == nil {
+ return nil, nil
+ }
+
+ // Return a copy of the vote.
+ vote := *voter.vote
+ return &vote, nil
+}
diff --git a/internal/praefect/transactions/transaction.go b/internal/praefect/transactions/transaction.go
index 2476c0b31..c3e480ac1 100644
--- a/internal/praefect/transactions/transaction.go
+++ b/internal/praefect/transactions/transaction.go
@@ -59,6 +59,8 @@ type Transaction interface {
State() (map[string]VoteResult, error)
// DidCommitAnySubtransaction returns whether the transaction committed at least one subtransaction.
DidCommitAnySubtransaction() bool
+ // DidVote returns whether the given node has cast a vote.
+ DidVote(string) bool
}
// transaction is a session where a set of voters votes on one or more
@@ -208,6 +210,28 @@ func (t *transaction) DidCommitAnySubtransaction() bool {
return false
}
+// DidVote determines whether the given node did cast a vote. If it's not possible to retrieve the
+// vote, then the node by definition didn't cast a vote.
+func (t *transaction) DidVote(node string) bool {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ // If there are no subtransactions, then no vote could've been cast by the given node.
+ if len(t.subtransactions) == 0 {
+ return false
+ }
+
+ // It's sufficient to take a look at the first transaction.
+ vote, err := t.subtransactions[0].getVote(node)
+ if err != nil {
+ // If it's not possible to retrieve the vote, then we consider the note to not have
+ // cast a vote.
+ return false
+ }
+
+ return vote != nil
+}
+
// getOrCreateSubtransaction gets an ongoing subtransaction on which the given
// node hasn't yet voted on or creates a new one if the node has succeeded on
// all subtransactions. In case the node has failed on any of the
diff --git a/internal/praefect/transactions/transaction_test.go b/internal/praefect/transactions/transaction_test.go
index 38828396a..93b636bd3 100644
--- a/internal/praefect/transactions/transaction_test.go
+++ b/internal/praefect/transactions/transaction_test.go
@@ -24,3 +24,30 @@ func TestTransactionCancellationWithEmptyTransaction(t *testing.T) {
require.Error(t, err)
require.Equal(t, err, ErrTransactionCanceled)
}
+
+func TestTransaction_DidVote(t *testing.T) {
+ ctx, cleanup := testhelper.Context()
+ defer cleanup()
+
+ tx, err := newTransaction(1, []Voter{
+ {Name: "v1", Votes: 1},
+ {Name: "v2", Votes: 0},
+ }, 1)
+ require.NoError(t, err)
+
+ // An unregistered voter did not vote.
+ require.False(t, tx.DidVote("unregistered"))
+ // And neither of the registered ones did cast a vote yet.
+ require.False(t, tx.DidVote("v1"))
+ require.False(t, tx.DidVote("v2"))
+
+ // One of both nodes does cast a vote.
+ require.NoError(t, tx.vote(ctx, "v1", voting.VoteFromData([]byte{})))
+ require.True(t, tx.DidVote("v1"))
+ require.False(t, tx.DidVote("v2"))
+
+ // And now the second node does cast a vote, too.
+ require.NoError(t, tx.vote(ctx, "v2", voting.VoteFromData([]byte{})))
+ require.True(t, tx.DidVote("v1"))
+ require.True(t, tx.DidVote("v2"))
+}