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-05-27 12:19:24 +0300
committerSami Hiltunen <shiltunen@gitlab.com>2023-07-13 10:38:12 +0300
commit27514a32311f50610568970a1074f0b6af20d504 (patch)
tree7a1e43bcc8119342a104201934ff716bb4a8ed62
parent997d60d56a5a1b5147f9a9b3db2afb79755466a2 (diff)
Always initialize staging directory for a transaction
So far we've been initializing the staging directory only when a transaction is about to write objects. Since we're soon about to set up a quarantine automatically when the transaction begins, we'll need the staging directory to store it. Let's create the staging directory thus unconditionally. Tests had to be adapted as the assertions ensuring the staging directory is always cleaned up were failing. Some of the tests were not always finishing their transactions. This was fine as they didn't write objects so the staging directory was not created and there was no state to clean up. Now that we always create it, we need to finish the transactions so they clean up after themselves. If there is a crash, some transactions could be interrupted without being able to clean up after themselves. This is handled in the PartitionManager by always deleting and recreating the staging directory on partition start to ensure it doesn't contain any stale data. Do the same in the TransactionManager tests to clean up after crashes. Previously read-only transactions would not set up a quarantine to avoid wasteful work that is not needed. They'll do so for now but we'll later fix this by differentiating read-only transactions from read-write transactions.
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager.go27
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_test.go16
2 files changed, 21 insertions, 22 deletions
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager.go b/internal/gitaly/storage/storagemgr/transaction_manager.go
index eeb167a71..ef08b33f6 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager.go
@@ -165,9 +165,6 @@ type Transaction struct {
// to finish where needed.
finished chan struct{}
- // initStagingDirectory is called to lazily initialize the staging directory when it is
- // needed.
- initStagingDirectory func() error
// stagingDirectory is the directory where the transaction stages its files prior
// to them being logged. It is cleaned up when the transaction finishes.
stagingDirectory string
@@ -240,16 +237,6 @@ func (mgr *TransactionManager) Begin(ctx context.Context) (_ *Transaction, retur
close(readReady)
}
- txn.initStagingDirectory = func() error {
- stagingDirectory, err := os.MkdirTemp(mgr.stagingDirectory, "")
- if err != nil {
- return fmt.Errorf("mkdir temp: %w", err)
- }
-
- txn.stagingDirectory = stagingDirectory
- return nil
- }
-
txn.finish = func() error {
defer close(txn.finished)
@@ -284,6 +271,12 @@ func (mgr *TransactionManager) Begin(ctx context.Context) (_ *Transaction, retur
return nil, ErrRepositoryNotFound
}
+ var err error
+ txn.stagingDirectory, err = os.MkdirTemp(mgr.stagingDirectory, "")
+ if err != nil {
+ return nil, fmt.Errorf("mkdir temp: %w", err)
+ }
+
return txn, nil
}
}
@@ -373,10 +366,6 @@ func (txn *Transaction) DeleteRepository() {
// is a Git object directory where the new objects introduced in the transaction must be written. The quarantined
// objects needed by the updated reference tips will be included in the transaction.
func (txn *Transaction) QuarantineDirectory() (string, error) {
- if err := txn.initStagingDirectory(); err != nil {
- return "", fmt.Errorf("init staging directory: %w", err)
- }
-
quarantineDirectory := filepath.Join(txn.stagingDirectory, "quarantine")
if err := os.MkdirAll(filepath.Join(quarantineDirectory, "pack"), perm.PrivateDir); err != nil {
return "", fmt.Errorf("create quarantine directory: %w", err)
@@ -595,10 +584,6 @@ func (mgr *TransactionManager) stageHooks(ctx context.Context, transaction *Tran
return nil
}
- if err := transaction.initStagingDirectory(); err != nil {
- return fmt.Errorf("init staging directory: %w", err)
- }
-
if err := repoutil.ExtractHooks(
ctx,
bytes.NewReader(transaction.customHooksUpdate.CustomHooksTAR),
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
index bbf4fb8b7..8b463f5cf 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager_test.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
@@ -1256,6 +1256,9 @@ func TestTransactionManager(t *testing.T) {
CustomHookIndex: 2,
},
},
+ Rollback{
+ TransactionID: 4,
+ },
},
expectedState: StateAssertion{
Database: DatabaseState{
@@ -1965,6 +1968,12 @@ func TestTransactionManager(t *testing.T) {
CustomHookIndex: 3,
},
},
+ Rollback{
+ TransactionID: 5,
+ },
+ Rollback{
+ TransactionID: 6,
+ },
},
expectedState: StateAssertion{
DefaultBranch: "refs/heads/main",
@@ -3065,8 +3074,13 @@ func TestTransactionManager(t *testing.T) {
managerRunning = true
managerErr = make(chan error)
- transactionManager = NewTransactionManager(database, storagePath, relativePath, stagingDir, setup.CommandFactory, housekeepingManager, setup.RepositoryFactory, noopTransactionFinalizer)
+ // The PartitionManager deletes and recreates the staging directory prior to starting a TransactionManager
+ // to clean up any stale state leftover by crashes. Do that here as well so the tests don't fail if we don't
+ // finish transactions after crash simulations.
+ require.NoError(t, os.RemoveAll(stagingDir))
+ require.NoError(t, os.Mkdir(stagingDir, perm.PrivateDir))
+ transactionManager = NewTransactionManager(database, storagePath, relativePath, stagingDir, setup.CommandFactory, housekeepingManager, setup.RepositoryFactory, noopTransactionFinalizer)
installHooks(t, transactionManager, database, hooks{
beforeReadLogEntry: step.Hooks.BeforeApplyLogEntry,
beforeStoreLogEntry: step.Hooks.BeforeAppendLogEntry,