gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }