- Imporve memeoery footprint

- Add batch processing for arr repairs
This commit is contained in:
Mukhtar Akere
2025-08-21 03:32:46 +01:00
parent 2548c21e5b
commit b0a698f15e
3 changed files with 85 additions and 22 deletions

View File

@@ -234,6 +234,35 @@ func (a *Arr) searchRadarr(files []ContentFile) error {
} }
func (a *Arr) SearchMissing(files []ContentFile) error { func (a *Arr) SearchMissing(files []ContentFile) error {
if len(files) == 0 {
return nil
}
return a.batchSearchMissing(files)
}
func (a *Arr) batchSearchMissing(files []ContentFile) error {
if len(files) == 0 {
return nil
}
BatchSize := 50
// Batch search for missing files
if len(files) > BatchSize {
for i := 0; i < len(files); i += BatchSize {
end := i + BatchSize
if end > len(files) {
end = len(files)
}
if err := a.searchMissing(files[i:end]); err != nil {
// continue searching the rest of the files
continue
}
}
return nil
}
return a.searchMissing(files)
}
func (a *Arr) searchMissing(files []ContentFile) error {
switch a.Type { switch a.Type {
case Sonarr: case Sonarr:
return a.searchSonarr(files) return a.searchSonarr(files)
@@ -245,6 +274,28 @@ func (a *Arr) SearchMissing(files []ContentFile) error {
} }
func (a *Arr) DeleteFiles(files []ContentFile) error { func (a *Arr) DeleteFiles(files []ContentFile) error {
if len(files) == 0 {
return nil
}
BatchSize := 50
// Batch delete files
if len(files) > BatchSize {
for i := 0; i < len(files); i += BatchSize {
end := i + BatchSize
if end > len(files) {
end = len(files)
}
if err := a.batchDeleteFiles(files[i:end]); err != nil {
// continue deleting the rest of the files
continue
}
}
return nil
}
return a.batchDeleteFiles(files)
}
func (a *Arr) batchDeleteFiles(files []ContentFile) error {
ids := make([]int, 0) ids := make([]int, 0)
for _, f := range files { for _, f := range files {
ids = append(ids, f.FileId) ids = append(ids, f.FileId)

View File

@@ -534,7 +534,7 @@ func (c *Cache) setTorrent(t CachedTorrent, callback func(torrent CachedTorrent)
mergedFiles := mergeFiles(o, updatedTorrent) // Useful for merging files across multiple torrents, while keeping the most recent mergedFiles := mergeFiles(o, updatedTorrent) // Useful for merging files across multiple torrents, while keeping the most recent
updatedTorrent.Files = mergedFiles updatedTorrent.Files = mergedFiles
} }
c.torrents.set(torrentName, t, updatedTorrent) c.torrents.set(torrentName, t)
go c.SaveTorrent(t) go c.SaveTorrent(t)
if callback != nil { if callback != nil {
go callback(updatedTorrent) go callback(updatedTorrent)
@@ -550,7 +550,7 @@ func (c *Cache) setTorrents(torrents map[string]CachedTorrent, callback func())
mergedFiles := mergeFiles(o, updatedTorrent) mergedFiles := mergeFiles(o, updatedTorrent)
updatedTorrent.Files = mergedFiles updatedTorrent.Files = mergedFiles
} }
c.torrents.set(torrentName, t, updatedTorrent) c.torrents.set(torrentName, t)
} }
c.SaveTorrents() c.SaveTorrents()
if callback != nil { if callback != nil {

View File

@@ -42,8 +42,8 @@ type directoryFilter struct {
type torrents struct { type torrents struct {
sync.RWMutex sync.RWMutex
byID map[string]CachedTorrent storage map[string]CachedTorrent // id to CachedTorrent
byName map[string]CachedTorrent byName map[string]string // name to id
} }
type folders struct { type folders struct {
@@ -72,8 +72,8 @@ func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
tc := &torrentCache{ tc := &torrentCache{
torrents: torrents{ torrents: torrents{
byID: make(map[string]CachedTorrent), storage: make(map[string]CachedTorrent), // id to CachedTorrent
byName: make(map[string]CachedTorrent), byName: make(map[string]string),
}, },
folders: folders{ folders: folders{
listing: make(map[string][]os.FileInfo), listing: make(map[string][]os.FileInfo),
@@ -88,8 +88,8 @@ func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
func (tc *torrentCache) reset() { func (tc *torrentCache) reset() {
tc.torrents.Lock() tc.torrents.Lock()
tc.torrents.byID = make(map[string]CachedTorrent) tc.torrents.byName = make(map[string]string)
tc.torrents.byName = make(map[string]CachedTorrent) tc.torrents.storage = make(map[string]CachedTorrent) // Reset the storage map
tc.torrents.Unlock() tc.torrents.Unlock()
// reset the sorted listing // reset the sorted listing
@@ -105,23 +105,27 @@ func (tc *torrentCache) reset() {
func (tc *torrentCache) getByID(id string) (CachedTorrent, bool) { func (tc *torrentCache) getByID(id string) (CachedTorrent, bool) {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
torrent, exists := tc.torrents.byID[id] torrent, exists := tc.torrents.storage[id]
return torrent, exists return torrent, exists
} }
func (tc *torrentCache) getByName(name string) (CachedTorrent, bool) { func (tc *torrentCache) getByName(name string) (CachedTorrent, bool) {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
torrent, exists := tc.torrents.byName[name] torrentID, exists := tc.torrents.byName[name]
if !exists {
return CachedTorrent{}, false
}
torrent, exists := tc.torrents.storage[torrentID]
return torrent, exists return torrent, exists
} }
func (tc *torrentCache) set(name string, torrent, newTorrent CachedTorrent) { func (tc *torrentCache) set(name string, torrent CachedTorrent) {
tc.torrents.Lock() tc.torrents.Lock()
// Set the id first // Set the id first
tc.torrents.byName[name] = torrent tc.torrents.byName[name] = torrent.Id
tc.torrents.byID[torrent.Id] = torrent // This is the unadulterated torrent tc.torrents.storage[torrent.Id] = torrent
tc.torrents.Unlock() tc.torrents.Unlock()
tc.sortNeeded.Store(true) tc.sortNeeded.Store(true)
} }
@@ -154,7 +158,11 @@ func (tc *torrentCache) refreshListing() {
tc.torrents.RLock() tc.torrents.RLock()
all := make([]sortableFile, 0, len(tc.torrents.byName)) all := make([]sortableFile, 0, len(tc.torrents.byName))
for name, t := range tc.torrents.byName { for name, torrentID := range tc.torrents.byName {
t, exists := tc.torrents.storage[torrentID]
if !exists {
continue // Skip if torrent not found
}
all = append(all, sortableFile{t.Id, name, t.AddedOn, t.Bytes, t.Bad}) all = append(all, sortableFile{t.Id, name, t.AddedOn, t.Bytes, t.Bad})
} }
tc.sortNeeded.Store(false) tc.sortNeeded.Store(false)
@@ -280,9 +288,9 @@ func (tc *torrentCache) torrentMatchDirectory(filters []directoryFilter, file so
func (tc *torrentCache) getAll() map[string]CachedTorrent { func (tc *torrentCache) getAll() map[string]CachedTorrent {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
result := make(map[string]CachedTorrent, len(tc.torrents.byID)) result := make(map[string]CachedTorrent, len(tc.torrents.storage))
for name, torrent := range tc.torrents.byID { for torrentID, torrent := range tc.torrents.storage {
result[name] = torrent result[torrentID] = torrent
} }
return result return result
} }
@@ -290,14 +298,18 @@ func (tc *torrentCache) getAll() map[string]CachedTorrent {
func (tc *torrentCache) getAllCount() int { func (tc *torrentCache) getAllCount() int {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
return len(tc.torrents.byID) return len(tc.torrents.storage)
} }
func (tc *torrentCache) getAllByName() map[string]CachedTorrent { func (tc *torrentCache) getAllByName() map[string]CachedTorrent {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
results := make(map[string]CachedTorrent, len(tc.torrents.byName)) results := make(map[string]CachedTorrent, len(tc.torrents.byName))
for name, torrent := range tc.torrents.byName { for name, torrentID := range tc.torrents.byName {
torrent, exists := tc.torrents.storage[torrentID]
if !exists {
continue // Skip if torrent not found
}
results[name] = torrent results[name] = torrent
} }
return results return results
@@ -306,8 +318,8 @@ func (tc *torrentCache) getAllByName() map[string]CachedTorrent {
func (tc *torrentCache) getIdMaps() map[string]struct{} { func (tc *torrentCache) getIdMaps() map[string]struct{} {
tc.torrents.RLock() tc.torrents.RLock()
defer tc.torrents.RUnlock() defer tc.torrents.RUnlock()
res := make(map[string]struct{}, len(tc.torrents.byID)) res := make(map[string]struct{}, len(tc.torrents.storage))
for id := range tc.torrents.byID { for id := range tc.torrents.storage {
res[id] = struct{}{} res[id] = struct{}{}
} }
return res return res
@@ -316,7 +328,7 @@ func (tc *torrentCache) getIdMaps() map[string]struct{} {
func (tc *torrentCache) removeId(id string) { func (tc *torrentCache) removeId(id string) {
tc.torrents.Lock() tc.torrents.Lock()
defer tc.torrents.Unlock() defer tc.torrents.Unlock()
delete(tc.torrents.byID, id) delete(tc.torrents.storage, id)
tc.sortNeeded.Store(true) tc.sortNeeded.Store(true)
} }