mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-07 05:42:42 +00:00
Reimplement DB collections for mirrors, repos and snapshots
See #765, #761 Collections were relying on keeping in-memory list of all the objects for any kind of operation which doesn't scale well the number of objects in the database. With this rewrite, objects are loaded only on demand which might be pessimization in some edge cases but should improve performance and memory footprint signifcantly.
This commit is contained in:
+70
-56
@@ -2,6 +2,7 @@ package deb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
@@ -93,8 +94,8 @@ func (repo *LocalRepo) RefKey() []byte {
|
||||
// LocalRepoCollection does listing, updating/adding/deleting of LocalRepos
|
||||
type LocalRepoCollection struct {
|
||||
*sync.RWMutex
|
||||
db database.Storage
|
||||
list []*LocalRepo
|
||||
db database.Storage
|
||||
cache map[string]*LocalRepo
|
||||
}
|
||||
|
||||
// NewLocalRepoCollection loads LocalRepos from DB and makes up collection
|
||||
@@ -102,43 +103,59 @@ func NewLocalRepoCollection(db database.Storage) *LocalRepoCollection {
|
||||
return &LocalRepoCollection{
|
||||
RWMutex: &sync.RWMutex{},
|
||||
db: db,
|
||||
cache: make(map[string]*LocalRepo),
|
||||
}
|
||||
}
|
||||
|
||||
func (collection *LocalRepoCollection) loadList() {
|
||||
if collection.list != nil {
|
||||
return
|
||||
}
|
||||
|
||||
blobs := collection.db.FetchByPrefix([]byte("L"))
|
||||
collection.list = make([]*LocalRepo, 0, len(blobs))
|
||||
|
||||
for _, blob := range blobs {
|
||||
r := &LocalRepo{}
|
||||
if err := r.Decode(blob); err != nil {
|
||||
log.Printf("Error decoding repo: %s\n", err)
|
||||
} else {
|
||||
collection.list = append(collection.list, r)
|
||||
func (collection *LocalRepoCollection) search(filter func(*LocalRepo) bool, unique bool) []*LocalRepo {
|
||||
result := []*LocalRepo(nil)
|
||||
for _, r := range collection.cache {
|
||||
if filter(r) {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
|
||||
if unique && len(result) > 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
collection.db.ProcessByPrefix([]byte("L"), func(key, blob []byte) error {
|
||||
r := &LocalRepo{}
|
||||
if err := r.Decode(blob); err != nil {
|
||||
log.Printf("Error decoding local repo: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if filter(r) {
|
||||
if _, exists := collection.cache[r.UUID]; !exists {
|
||||
collection.cache[r.UUID] = r
|
||||
result = append(result, r)
|
||||
if unique {
|
||||
return errors.New("abort")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Add appends new repo to collection and saves it
|
||||
func (collection *LocalRepoCollection) Add(repo *LocalRepo) error {
|
||||
collection.loadList()
|
||||
_, err := collection.ByName(repo.Name)
|
||||
|
||||
for _, r := range collection.list {
|
||||
if r.Name == repo.Name {
|
||||
return fmt.Errorf("local repo with name %s already exists", repo.Name)
|
||||
}
|
||||
if err == nil {
|
||||
return fmt.Errorf("local repo with name %s already exists", repo.Name)
|
||||
}
|
||||
|
||||
err := collection.Update(repo)
|
||||
err = collection.Update(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
collection.list = append(collection.list, repo)
|
||||
collection.cache[repo.UUID] = repo
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -159,8 +176,6 @@ func (collection *LocalRepoCollection) Update(repo *LocalRepo) error {
|
||||
|
||||
// LoadComplete loads additional information for local repo
|
||||
func (collection *LocalRepoCollection) LoadComplete(repo *LocalRepo) error {
|
||||
collection.loadList()
|
||||
|
||||
encoded, err := collection.db.Get(repo.RefKey())
|
||||
if err == database.ErrNotFound {
|
||||
return nil
|
||||
@@ -175,26 +190,39 @@ func (collection *LocalRepoCollection) LoadComplete(repo *LocalRepo) error {
|
||||
|
||||
// ByName looks up repository by name
|
||||
func (collection *LocalRepoCollection) ByName(name string) (*LocalRepo, error) {
|
||||
collection.loadList()
|
||||
|
||||
for _, r := range collection.list {
|
||||
if r.Name == name {
|
||||
return r, nil
|
||||
}
|
||||
result := collection.search(func(r *LocalRepo) bool { return r.Name == name }, true)
|
||||
if len(result) == 0 {
|
||||
return nil, fmt.Errorf("local repo with name %s not found", name)
|
||||
}
|
||||
return nil, fmt.Errorf("local repo with name %s not found", name)
|
||||
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// ByUUID looks up repository by uuid
|
||||
func (collection *LocalRepoCollection) ByUUID(uuid string) (*LocalRepo, error) {
|
||||
collection.loadList()
|
||||
|
||||
for _, r := range collection.list {
|
||||
if r.UUID == uuid {
|
||||
return r, nil
|
||||
}
|
||||
if r, ok := collection.cache[uuid]; ok {
|
||||
return r, nil
|
||||
}
|
||||
return nil, fmt.Errorf("local repo with uuid %s not found", uuid)
|
||||
|
||||
key := (&LocalRepo{UUID: uuid}).Key()
|
||||
|
||||
value, err := collection.db.Get(key)
|
||||
if err == database.ErrNotFound {
|
||||
return nil, fmt.Errorf("local repo with uuid %s not found", uuid)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &LocalRepo{}
|
||||
err = r.Decode(value)
|
||||
|
||||
if err == nil {
|
||||
collection.cache[r.UUID] = r
|
||||
}
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
// ForEach runs method for each repository
|
||||
@@ -212,30 +240,16 @@ func (collection *LocalRepoCollection) ForEach(handler func(*LocalRepo) error) e
|
||||
|
||||
// Len returns number of remote repos
|
||||
func (collection *LocalRepoCollection) Len() int {
|
||||
collection.loadList()
|
||||
|
||||
return len(collection.list)
|
||||
return len(collection.db.KeysByPrefix([]byte("L")))
|
||||
}
|
||||
|
||||
// Drop removes remote repo from collection
|
||||
func (collection *LocalRepoCollection) Drop(repo *LocalRepo) error {
|
||||
collection.loadList()
|
||||
|
||||
repoPosition := -1
|
||||
|
||||
for i, r := range collection.list {
|
||||
if r == repo {
|
||||
repoPosition = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if repoPosition == -1 {
|
||||
if _, err := collection.db.Get(repo.Key()); err == database.ErrNotFound {
|
||||
panic("local repo not found!")
|
||||
}
|
||||
|
||||
collection.list[len(collection.list)-1], collection.list[repoPosition], collection.list =
|
||||
nil, collection.list[len(collection.list)-1], collection.list[:len(collection.list)-1]
|
||||
delete(collection.cache, repo.UUID)
|
||||
|
||||
err := collection.db.Delete(repo.Key())
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user