prepare.go (3897B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package timeline 19 20 import ( 21 "container/list" 22 "context" 23 "errors" 24 25 "codeberg.org/gruf/go-kv" 26 "github.com/superseriousbusiness/gotosocial/internal/db" 27 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 28 "github.com/superseriousbusiness/gotosocial/internal/log" 29 ) 30 31 func (t *timeline) prepareXBetweenIDs(ctx context.Context, amount int, behindID string, beforeID string, frontToBack bool) error { 32 l := log. 33 WithContext(ctx). 34 WithFields(kv.Fields{ 35 {"amount", amount}, 36 {"behindID", behindID}, 37 {"beforeID", beforeID}, 38 {"frontToBack", frontToBack}, 39 }...) 40 l.Trace("entering prepareXBetweenIDs") 41 42 if beforeID >= behindID { 43 // This is an impossible situation, we 44 // can't prepare anything between these. 45 return nil 46 } 47 48 if err := t.indexXBetweenIDs(ctx, amount, behindID, beforeID, frontToBack); err != nil { 49 // An error here doesn't necessarily mean we 50 // can't prepare anything, so log + keep going. 51 l.Debugf("error calling prepareXBetweenIDs: %s", err) 52 } 53 54 t.Lock() 55 defer t.Unlock() 56 57 // Try to prepare everything between (and including) the two points. 58 var ( 59 toPrepare = make(map[*list.Element]*indexedItemsEntry) 60 foundToPrepare int 61 ) 62 63 if frontToBack { 64 // Paging forwards / down. 65 for e := t.items.data.Front(); e != nil; e = e.Next() { 66 entry := e.Value.(*indexedItemsEntry) //nolint:forcetypeassert 67 68 if entry.itemID > behindID { 69 l.Trace("item is too new, continuing") 70 continue 71 } 72 73 if entry.itemID < beforeID { 74 // We've gone beyond the bounds of 75 // items we're interested in; stop. 76 l.Trace("reached older items, breaking") 77 break 78 } 79 80 // Only prepare entry if it's not 81 // already prepared, save db calls. 82 if entry.prepared == nil { 83 toPrepare[e] = entry 84 } 85 86 foundToPrepare++ 87 if foundToPrepare >= amount { 88 break 89 } 90 } 91 } else { 92 // Paging backwards / up. 93 for e := t.items.data.Back(); e != nil; e = e.Prev() { 94 entry := e.Value.(*indexedItemsEntry) //nolint:forcetypeassert 95 96 if entry.itemID < beforeID { 97 l.Trace("item is too old, continuing") 98 continue 99 } 100 101 if entry.itemID > behindID { 102 // We've gone beyond the bounds of 103 // items we're interested in; stop. 104 l.Trace("reached newer items, breaking") 105 break 106 } 107 108 if entry.prepared == nil { 109 toPrepare[e] = entry 110 } 111 112 // Only prepare entry if it's not 113 // already prepared, save db calls. 114 foundToPrepare++ 115 if foundToPrepare >= amount { 116 break 117 } 118 } 119 } 120 121 for e, entry := range toPrepare { 122 prepared, err := t.prepareFunction(ctx, t.timelineID, entry.itemID) 123 if err != nil { 124 if errors.Is(err, db.ErrNoEntries) { 125 // ErrNoEntries means something has been deleted, 126 // so we'll likely not be able to ever prepare this. 127 // This means we can remove it and skip past it. 128 l.Debugf("db.ErrNoEntries while trying to prepare %s; will remove from timeline", entry.itemID) 129 t.items.data.Remove(e) 130 } 131 // We've got a proper db error. 132 return gtserror.Newf("db error while trying to prepare %s: %w", entry.itemID, err) 133 } 134 entry.prepared = prepared 135 } 136 137 return nil 138 }