Consistently use transactions to update database

For any action which is multi-step (requires updating more than 1 DB
key), use transaction to make update atomic.

Also pack big chunks of updates (importing packages for importing and
mirror updates) into single transaction to improve aptly performance and
get some isolation.

Note that still layers up (Collections) provide some level of isolation,
so this is going to shine with the future PRs to remove collection
locks.

Spin-off of #459
This commit is contained in:
Andrey Smirnov
2019-08-09 22:34:55 +03:00
committed by Andrey Smirnov
parent 67e38955ae
commit 77d7c3871a
19 changed files with 187 additions and 68 deletions
+45 -15
View File
@@ -617,34 +617,44 @@ func (repo *RemoteRepo) BuildDownloadQueue(packagePool aptly.PackagePool, packag
// FinalizeDownload swaps for final value of package refs
func (repo *RemoteRepo) FinalizeDownload(collectionFactory *CollectionFactory, progress aptly.Progress) error {
transaction, err := collectionFactory.PackageCollection().db.OpenTransaction()
if err != nil {
return err
}
defer transaction.Discard()
repo.LastDownloadDate = time.Now()
if progress != nil {
progress.InitBar(int64(repo.packageList.Len()), true)
progress.InitBar(int64(repo.packageList.Len()), false)
}
var i int
// update all the packages in collection
err := repo.packageList.ForEach(func(p *Package) error {
err = repo.packageList.ForEach(func(p *Package) error {
i++
if progress != nil {
progress.SetBar(i)
}
// download process might have updated checksums
p.UpdateFiles(p.Files())
return collectionFactory.PackageCollection().Update(p)
return collectionFactory.PackageCollection().UpdateInTransaction(p, transaction)
})
repo.packageRefs = NewPackageRefListFromPackageList(repo.packageList)
if err == nil {
repo.packageRefs = NewPackageRefListFromPackageList(repo.packageList)
repo.packageList = nil
}
if progress != nil {
progress.ShutdownBar()
}
repo.packageList = nil
return err
if err != nil {
return err
}
return transaction.Commit()
}
// Encode does msgpack encoding of RemoteRepo
@@ -795,17 +805,24 @@ func (collection *RemoteRepoCollection) Add(repo *RemoteRepo) error {
// Update stores updated information about repo in DB
func (collection *RemoteRepoCollection) Update(repo *RemoteRepo) error {
err := collection.db.Put(repo.Key(), repo.Encode())
transaction, err := collection.db.OpenTransaction()
if err != nil {
return err
}
defer transaction.Discard()
err = transaction.Put(repo.Key(), repo.Encode())
if err != nil {
return err
}
if repo.packageRefs != nil {
err = collection.db.Put(repo.RefKey(), repo.packageRefs.Encode())
err = transaction.Put(repo.RefKey(), repo.packageRefs.Encode())
if err != nil {
return err
}
}
return nil
return transaction.Commit()
}
// LoadComplete loads additional information for remote repo
@@ -878,16 +895,29 @@ func (collection *RemoteRepoCollection) Len() int {
// Drop removes remote repo from collection
func (collection *RemoteRepoCollection) Drop(repo *RemoteRepo) error {
if _, err := collection.db.Get(repo.Key()); err == database.ErrNotFound {
panic("repo not found!")
transaction, err := collection.db.OpenTransaction()
if err != nil {
return err
}
defer transaction.Discard()
if _, err = transaction.Get(repo.Key()); err != nil {
if err == database.ErrNotFound {
return errors.New("repo not found")
}
return err
}
delete(collection.cache, repo.UUID)
err := collection.db.Delete(repo.Key())
if err != nil {
if err = transaction.Delete(repo.Key()); err != nil {
return err
}
return collection.db.Delete(repo.RefKey())
if err = transaction.Delete(repo.RefKey()); err != nil {
return err
}
return transaction.Commit()
}