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:
authorSami Hiltunen <shiltunen@gitlab.com>2023-10-02 16:55:51 +0300
committerSami Hiltunen <shiltunen@gitlab.com>2023-11-14 16:36:01 +0300
commit5f746051981c41aab8ec4d169b0e556500220d34 (patch)
treeefc32440b68620e6194f773d5f86da09acf292f4
parentadc156fe95433002eb33ffb1c13cd2d267d14c70 (diff)
Verify references against a snapshot
TransactionManager is currently verifying references against the main repository. The verification process prepares a reference transaction and considers the verification successful if no errors come up. Preparing a reference transaction in the repository may leave locks if there is a crash while the locks are held. As the verification happens prior to logging the write, there's no record of the fact that locks may have been left in the repository. As it is, such locks would be left in the repository, and never cleaned up. This commit runs verifies the references against a snapshot of the target repository. This has few benefits: 1. The locks target the snapshot. If there is a crash while the locks are held, they'll be removed along with the snapshot when Gitaly restarts. 2. As the verification process no longer modifies the repository, transactions can take their snapshots concurrently with the reference verification running. 3. This paves the way for verifying more complex transactions by using multiple reference transactions. This is useful for example when a transaction resolves a directory-file conflict. Such updates can't be done in a single reference transaction. In future we can also verify transactions and apply log to the repository concurrently as the verification process no longer writes the locks to the repository. This will especially improve throughput with Raft as log entries don't have to be committed one by one anymore.
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager.go33
1 files changed, 17 insertions, 16 deletions
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager.go b/internal/gitaly/storage/storagemgr/transaction_manager.go
index 78ba2fd5b..3a81848c8 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager.go
@@ -293,8 +293,6 @@ func (mgr *TransactionManager) Begin(ctx context.Context, relativePath string, s
return nil, fmt.Errorf("mkdir temp: %w", err)
}
- mgr.stateLock.RLock()
- defer mgr.stateLock.RUnlock()
if txn.snapshot, err = newSnapshot(ctx,
mgr.storagePath,
filepath.Join(txn.stagingDirectory, "snapshot"),
@@ -658,9 +656,6 @@ type TransactionManager struct {
// Run and Begin which are ran in different goroutines.
mutex sync.Mutex
- // stateLock is used to synchronize snapshotting with reference verification as the verification
- // process targets the main repository and creates locks in it.
- stateLock sync.RWMutex
// snapshotLocks contains state used for synchronizing snapshotters with the log application.
snapshotLocks map[LSN]*snapshotLock
@@ -1521,7 +1516,7 @@ func (mgr *TransactionManager) verifyReferences(ctx context.Context, transaction
) == -1
})
- if err := mgr.verifyReferencesWithGit(ctx, referenceUpdates, transaction.stagingRepository); err != nil {
+ if err := mgr.verifyReferencesWithGit(ctx, referenceUpdates, transaction); err != nil {
return nil, fmt.Errorf("verify references with git: %w", err)
}
@@ -1532,17 +1527,23 @@ func (mgr *TransactionManager) verifyReferences(ctx context.Context, transaction
// the updates will go through when they are being applied in the log. This also catches any invalid reference names
// and file/directory conflicts with Git's loose reference storage which can occur with references like
// 'refs/heads/parent' and 'refs/heads/parent/child'.
-func (mgr *TransactionManager) verifyReferencesWithGit(ctx context.Context, referenceUpdates []*gitalypb.LogEntry_ReferenceUpdate, quarantinedRepo *localrepo.Repo) error {
- // Prevent snapshots from being taken concurrently as the reference verification
- // currently creates locks in the repository. We don't need to synchronnize snapshotting
- // and reference verification once we are no longer verifying the references against
- // the repository.
- //
- // Alternatively this could be removed by ignoring the lock files during snapshot creation.
- mgr.stateLock.Lock()
- defer mgr.stateLock.Unlock()
+func (mgr *TransactionManager) verifyReferencesWithGit(ctx context.Context, referenceUpdates []*gitalypb.LogEntry_ReferenceUpdate, tx *Transaction) error {
+ relativePath := tx.stagingRepository.GetRelativePath()
+ snapshot, err := newSnapshot(ctx,
+ mgr.storagePath,
+ filepath.Join(tx.stagingDirectory, "staging"),
+ []string{relativePath},
+ )
+ if err != nil {
+ return fmt.Errorf("new snapshot: %w", err)
+ }
+
+ repo, err := mgr.repositoryFactory.Build(snapshot.relativePath(relativePath)).Quarantine(tx.quarantineDirectory)
+ if err != nil {
+ return fmt.Errorf("quarantine: %w", err)
+ }
- updater, err := mgr.prepareReferenceTransaction(ctx, referenceUpdates, quarantinedRepo)
+ updater, err := mgr.prepareReferenceTransaction(ctx, referenceUpdates, repo)
if err != nil {
return fmt.Errorf("prepare reference transaction: %w", err)
}