Skip to content

Commit fd1c7aa

Browse files
gregns1claude
andcommitted
CBG-5784: add test accessors for the channel cache
Adds the accessors db/channelcachetest needs to drive channelCacheImpl, singleChannelCacheImpl and the bypass cache from outside package db. Derived by attempting the move and compiling to closure - three probe rounds, then three more accessors surfaced during the move itself, because newSingleChannelCache and newChannelCache return unnameable types and mask every method call on their results. A single pass finds six of them. Notable shapes: singleChannelCacheImpl.options is already a *ChannelCacheOptions, so OptionsForTest is a plain getter and the twelve sites that adjust cache caps in place keep working. addChannelCache returns the concrete cache and a capacity flag rather than an error, so AddChannelCacheForTest mirrors that. WaitForChannelCacheCompactionForTest absorbs the waitForCompaction helper from channel_cache_test.go, whose *channelCacheImpl parameter cannot be named from another package. Two exceptions to the accessor-only rule, both because test code has to name a type rather than merely hold it - a type assertion, and a closure whose result type is the concrete cache. ChannelCacheImplForTest and SingleChannelCacheImplForTest are aliases for exactly those cases; no accessor can stand in for a result type. AddListenerToNewestLateLogForTest is the one accessor that is not a bare pass-through: it takes lateLogLock for precisely the operation TestLateLogsSpikeForcePrunedBoundsLateLogsAndForcesRollback performed inline, so the critical section is unchanged. Encapsulating it avoids stranding that test in package db the way TestAddPendingLogs is. No wrapper struct: an exported constructor returning an unexported type is usable by inference, so the 78 addToCache call sites need one exported method rather than a parallel type. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5a812c7 commit fd1c7aa

1 file changed

Lines changed: 122 additions & 0 deletions

File tree

db/util_testing.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,3 +1689,125 @@ func CachingFeedCollectionsForTest(_ testing.TB, metadataStore base.DataStore, s
16891689

16901690
// DefaultWaitForSequence is the feed wait time used when a test does not supply its own.
16911691
const DefaultWaitForSequence = defaultWaitForSequence
1692+
1693+
// The accessors below serve db/channelcachetest, which drives the channel cache and the single
1694+
// channel cache from outside package db. Same conventions as the change cache set above: thin
1695+
// pass-throughs, a leading testing.TB to mark them test-only, and a ForTest suffix so the whole
1696+
// surface is greppable.
1697+
1698+
// NewChannelCacheForTest builds a channel cache directly, bypassing the database that would
1699+
// normally own it.
1700+
func NewChannelCacheForTest(_ testing.TB, ctx context.Context, dbName string, options ChannelCacheOptions,
1701+
queryHandlerFactory ChannelQueryHandlerFactory, activeChannels *channels.ActiveChannels,
1702+
cacheStats *base.CacheStats) (*channelCacheImpl, error) {
1703+
return newChannelCache(ctx, dbName, options, queryHandlerFactory, activeChannels, cacheStats)
1704+
}
1705+
1706+
// NewSingleChannelCacheWithOptionsForTest builds a single channel cache with explicit cache
1707+
// options rather than the defaults.
1708+
func NewSingleChannelCacheWithOptionsForTest(_ testing.TB, ctx context.Context, queryHandler ChannelQueryHandler,
1709+
channel channels.ID, validFrom uint64, options ChannelCacheOptions, cacheStats *base.CacheStats) *singleChannelCacheImpl {
1710+
return newChannelCacheWithOptions(ctx, queryHandler, channel, validFrom, options, cacheStats)
1711+
}
1712+
1713+
// NewBypassChannelCacheForTest builds the bypass cache used when the channel cache is at capacity.
1714+
func NewBypassChannelCacheForTest(_ testing.TB, queryHandler ChannelQueryHandler, channel channels.ID) *bypassChannelCache {
1715+
return &bypassChannelCache{channel: channel, queryHandler: queryHandler}
1716+
}
1717+
1718+
// The two cache implementations are exported under aliases because some test code has to name
1719+
// the type rather than merely hold it: a type assertion, and a closure whose result type is the
1720+
// concrete cache. An accessor cannot stand in for either.
1721+
type ChannelCacheImplForTest = channelCacheImpl
1722+
type SingleChannelCacheImplForTest = singleChannelCacheImpl
1723+
1724+
// ChannelCachesForTest exposes the collection of per-channel caches.
1725+
func (c *channelCacheImpl) ChannelCachesForTest(_ testing.TB) *channels.RangeSafeCollection {
1726+
return c.channelCaches
1727+
}
1728+
1729+
// AddChannelCacheForTest adds a cache for the given channel, reporting false if the cache is at
1730+
// capacity.
1731+
func (c *channelCacheImpl) AddChannelCacheForTest(_ testing.TB, ctx context.Context, channel channels.ID) (*singleChannelCacheImpl, bool) {
1732+
return c.addChannelCache(ctx, channel)
1733+
}
1734+
1735+
// WaitForChannelCacheCompactionForTest polls until compaction has finished, reporting false if it
1736+
// did not complete in time.
1737+
func WaitForChannelCacheCompactionForTest(_ testing.TB, cache *channelCacheImpl) (compactionComplete bool) {
1738+
for i := 0; i <= 10; i++ {
1739+
if cache.compactRunning.IsTrue() {
1740+
time.Sleep(100 * time.Millisecond)
1741+
} else {
1742+
return true
1743+
}
1744+
}
1745+
return false
1746+
}
1747+
1748+
// CleanAgedLateLogsForTest runs the background age-based late log prune across all channels.
1749+
func (c *channelCacheImpl) CleanAgedLateLogsForTest(_ testing.TB, ctx context.Context) error {
1750+
return c.cleanAgedLateLogs(ctx)
1751+
}
1752+
1753+
// AddToCacheForTest caches a single entry directly, bypassing the change cache.
1754+
func (c *singleChannelCacheImpl) AddToCacheForTest(_ testing.TB, ctx context.Context, change *LogEntry, isRemoval bool) {
1755+
c.addToCache(ctx, change, isRemoval)
1756+
}
1757+
1758+
// PrependChangesForTest prepends historical changes to the cache, returning the number added.
1759+
func (c *singleChannelCacheImpl) PrependChangesForTest(_ testing.TB, ctx context.Context, changes LogEntries,
1760+
changesValidFrom uint64, changesValidTo uint64) int {
1761+
return c.prependChanges(ctx, changes, changesValidFrom, changesValidTo)
1762+
}
1763+
1764+
// OptionsForTest exposes the cache's size and expiry settings. The field is a pointer, so tests
1765+
// can adjust the caps in place as they did before.
1766+
func (c *singleChannelCacheImpl) OptionsForTest(_ testing.TB) *ChannelCacheOptions {
1767+
return c.options
1768+
}
1769+
1770+
// SetValidFromForTest sets the first sequence the cached log is valid for.
1771+
func (c *singleChannelCacheImpl) SetValidFromForTest(_ testing.TB, validFrom uint64) {
1772+
c.validFrom = validFrom
1773+
}
1774+
1775+
// LateLogCountForTest reports the number of late log entries held.
1776+
func (c *singleChannelCacheImpl) LateLogCountForTest(_ testing.TB) int64 {
1777+
return c.lateLogCount()
1778+
}
1779+
1780+
// CountedLateLogCountForTest reports the late log count as reflected in the stats gauge, which
1781+
// does not count the parked sentinel entry.
1782+
func (c *singleChannelCacheImpl) CountedLateLogCountForTest(_ testing.TB) int64 {
1783+
return c.countedLateLogCount()
1784+
}
1785+
1786+
// PruneLateLogAgeForTest drops late log entries older than the configured age.
1787+
func (c *singleChannelCacheImpl) PruneLateLogAgeForTest(_ testing.TB, ctx context.Context) {
1788+
c.pruneLateLogAge(ctx)
1789+
}
1790+
1791+
// SetRecentlyUsedForTest sets the recently-used flag that cache compaction reads.
1792+
func SetRecentlyUsedForTest(_ testing.TB, cache SingleChannelCache, recentlyUsed bool) {
1793+
cache.(*singleChannelCacheImpl).recentlyUsed.Set(recentlyUsed)
1794+
}
1795+
1796+
// AddListenerToNewestLateLogForTest registers a late-feed listener on the newest late log entry,
1797+
// holding lateLogLock for exactly the operation the test performed inline before the move.
1798+
func (c *singleChannelCacheImpl) AddListenerToNewestLateLogForTest(_ testing.TB) {
1799+
c.lateLogLock.Lock()
1800+
defer c.lateLogLock.Unlock()
1801+
c.lateLogs[len(c.lateLogs)-1].addListener()
1802+
}
1803+
1804+
// GetNextSequenceForTest reports the next consecutive sequence the change cache expects, taking
1805+
// the read lock as the underlying accessor does.
1806+
func (c *changeCache) GetNextSequenceForTest(_ testing.TB) uint64 {
1807+
return c.getNextSequence()
1808+
}
1809+
1810+
// GetOldestSkippedSequenceForTest reports the oldest sequence still pending on the caching feed.
1811+
func (c *changeCache) GetOldestSkippedSequenceForTest(_ testing.TB, ctx context.Context) uint64 {
1812+
return c.getOldestSkippedSequence(ctx)
1813+
}

0 commit comments

Comments
 (0)