mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 23:12:25 -05:00 
			
		
		
		
	[bugfix] update go-cache version to support multi-keying (#1756)
* update go-cache version to support multi-keying Signed-off-by: kim <grufwub@gmail.com> * improved cache invalidation Signed-off-by: kim <grufwub@gmail.com> --------- Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
		
					parent
					
						
							
								65cd1acbdf
							
						
					
				
			
			
				commit
				
					
						8275d70e38
					
				
			
		
					 8 changed files with 90 additions and 67 deletions
				
			
		
							
								
								
									
										2
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -5,7 +5,7 @@ go 1.20 | ||||||
| require ( | require ( | ||||||
| 	codeberg.org/gruf/go-bytesize v1.0.2 | 	codeberg.org/gruf/go-bytesize v1.0.2 | ||||||
| 	codeberg.org/gruf/go-byteutil v1.1.2 | 	codeberg.org/gruf/go-byteutil v1.1.2 | ||||||
| 	codeberg.org/gruf/go-cache/v3 v3.2.6 | 	codeberg.org/gruf/go-cache/v3 v3.3.0 | ||||||
| 	codeberg.org/gruf/go-debug v1.3.0 | 	codeberg.org/gruf/go-debug v1.3.0 | ||||||
| 	codeberg.org/gruf/go-errors/v2 v2.2.0 | 	codeberg.org/gruf/go-errors/v2 v2.2.0 | ||||||
| 	codeberg.org/gruf/go-fastcopy v1.1.2 | 	codeberg.org/gruf/go-fastcopy v1.1.2 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -49,8 +49,8 @@ codeberg.org/gruf/go-bytesize v1.0.2/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacp | ||||||
| codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | ||||||
| codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw= | codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw= | ||||||
| codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | ||||||
| codeberg.org/gruf/go-cache/v3 v3.2.6 h1:PtAGOvCTGwhqOqIEFBP4M0F6xbaAWYe3t/7QYGNzulI= | codeberg.org/gruf/go-cache/v3 v3.3.0 h1:Bor75j4MYJIDqH22/aQvmwA7hMqBOzDOWdSQz25Lq+8= | ||||||
| codeberg.org/gruf/go-cache/v3 v3.2.6/go.mod h1:pTeVPEb9DshXUkd8Dg76UcsLpU6EC/tXQ2qb+JrmxEc= | codeberg.org/gruf/go-cache/v3 v3.3.0/go.mod h1:pTeVPEb9DshXUkd8Dg76UcsLpU6EC/tXQ2qb+JrmxEc= | ||||||
| codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs= | codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs= | ||||||
| codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg= | codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg= | ||||||
| codeberg.org/gruf/go-errors/v2 v2.0.0/go.mod h1:ZRhbdhvgoUA3Yw6e56kd9Ox984RrvbEFC2pOXyHDJP4= | codeberg.org/gruf/go-errors/v2 v2.0.0/go.mod h1:ZRhbdhvgoUA3Yw6e56kd9Ox984RrvbEFC2pOXyHDJP4= | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							|  | @ -65,8 +65,9 @@ func (c *Caches) Stop() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // setuphooks sets necessary cache invalidation hooks between caches, | // setuphooks sets necessary cache invalidation hooks between caches, | ||||||
| // as an invalidation indicates a database UPDATE / DELETE. INSERT is | // as an invalidation indicates a database INSERT / UPDATE / DELETE. | ||||||
| // not handled by invalidation hooks and must be invalidated manually. | // NOTE THEY ARE ONLY CALLED WHEN THE ITEM IS IN THE CACHE, SO FOR | ||||||
|  | // HOOKS TO BE CALLED ON DELETE YOU MUST FIRST POPULATE IT IN THE CACHE. | ||||||
| func (c *Caches) setuphooks() { | func (c *Caches) setuphooks() { | ||||||
| 	c.GTS.Account().SetInvalidateCallback(func(account *gtsmodel.Account) { | 	c.GTS.Account().SetInvalidateCallback(func(account *gtsmodel.Account) { | ||||||
| 		// Invalidate account ID cached visibility. | 		// Invalidate account ID cached visibility. | ||||||
|  | @ -103,13 +104,18 @@ func (c *Caches) setuphooks() { | ||||||
| 		c.Visibility.Invalidate("ItemID", followReq.TargetAccountID) | 		c.Visibility.Invalidate("ItemID", followReq.TargetAccountID) | ||||||
| 		c.Visibility.Invalidate("RequesterID", followReq.TargetAccountID) | 		c.Visibility.Invalidate("RequesterID", followReq.TargetAccountID) | ||||||
| 
 | 
 | ||||||
| 		// Invalidate any cached follow corresponding to this request. | 		// Invalidate any cached follow with same ID. | ||||||
| 		c.GTS.Follow().Invalidate("AccountID.TargetAccountID", followReq.AccountID, followReq.TargetAccountID) | 		c.GTS.Follow().Invalidate("ID", followReq.ID) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	c.GTS.Status().SetInvalidateCallback(func(status *gtsmodel.Status) { | 	c.GTS.Status().SetInvalidateCallback(func(status *gtsmodel.Status) { | ||||||
| 		// Invalidate status ID cached visibility. | 		// Invalidate status ID cached visibility. | ||||||
| 		c.Visibility.Invalidate("ItemID", status.ID) | 		c.Visibility.Invalidate("ItemID", status.ID) | ||||||
|  | 
 | ||||||
|  | 		for _, id := range status.AttachmentIDs { | ||||||
|  | 			// Invalidate cache for attached media IDs, | ||||||
|  | 			c.GTS.Media().Invalidate("ID", id) | ||||||
|  | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	c.GTS.User().SetInvalidateCallback(func(user *gtsmodel.User) { | 	c.GTS.User().SetInvalidateCallback(func(user *gtsmodel.User) { | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								internal/cache/visibility.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								internal/cache/visibility.go
									
										
									
									
										vendored
									
									
								
							|  | @ -30,8 +30,8 @@ type VisibilityCache struct { | ||||||
| // NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe. | // NOTE: the cache MUST NOT be in use anywhere, this is not thread-safe. | ||||||
| func (c *VisibilityCache) Init() { | func (c *VisibilityCache) Init() { | ||||||
| 	c.Cache = result.New([]result.Lookup{ | 	c.Cache = result.New([]result.Lookup{ | ||||||
| 		{Name: "ItemID"}, | 		{Name: "ItemID", Multi: true}, | ||||||
| 		{Name: "RequesterID"}, | 		{Name: "RequesterID", Multi: true}, | ||||||
| 		{Name: "Type.RequesterID.ItemID"}, | 		{Name: "Type.RequesterID.ItemID"}, | ||||||
| 	}, func(v1 *CachedVisibility) *CachedVisibility { | 	}, func(v1 *CachedVisibility) *CachedVisibility { | ||||||
| 		v2 := new(CachedVisibility) | 		v2 := new(CachedVisibility) | ||||||
|  |  | ||||||
|  | @ -208,14 +208,17 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, sourceAccountI | ||||||
| 		Notify:          followReq.Notify, | 		Notify:          followReq.Notify, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err := r.state.Caches.GTS.Follow().Store(follow, func() error { | ||||||
| 		// If the follow already exists, just | 		// If the follow already exists, just | ||||||
| 		// replace the URI with the new one. | 		// replace the URI with the new one. | ||||||
| 	if _, err := r.conn. | 		_, err := r.conn. | ||||||
| 			NewInsert(). | 			NewInsert(). | ||||||
| 			Model(follow). | 			Model(follow). | ||||||
| 			On("CONFLICT (?,?) DO UPDATE set ? = ?", bun.Ident("account_id"), bun.Ident("target_account_id"), bun.Ident("uri"), follow.URI). | 			On("CONFLICT (?,?) DO UPDATE set ? = ?", bun.Ident("account_id"), bun.Ident("target_account_id"), bun.Ident("uri"), follow.URI). | ||||||
| 		Exec(ctx); err != nil { | 			Exec(ctx) | ||||||
| 		return nil, r.conn.ProcessError(err) | 		return r.conn.ProcessError(err) | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Delete original follow request. | 	// Delete original follow request. | ||||||
|  | @ -227,8 +230,7 @@ func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, sourceAccountI | ||||||
| 		return nil, r.conn.ProcessError(err) | 		return nil, r.conn.ProcessError(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Invalidate follow request from cache lookups; this will | 	// Invalidate follow request from cache lookups | ||||||
| 	// invalidate the follow as well via the invalidate hook. |  | ||||||
| 	r.state.Caches.GTS.FollowRequest().Invalidate("ID", followReq.ID) | 	r.state.Caches.GTS.FollowRequest().Invalidate("ID", followReq.ID) | ||||||
| 
 | 
 | ||||||
| 	// Delete original follow request notification | 	// Delete original follow request notification | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								vendor/codeberg.org/gruf/go-cache/v3/result/cache.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/codeberg.org/gruf/go-cache/v3/result/cache.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -19,6 +19,10 @@ type Lookup struct { | ||||||
| 	// under zero value keys, otherwise ignore them. | 	// under zero value keys, otherwise ignore them. | ||||||
| 	AllowZero bool | 	AllowZero bool | ||||||
| 
 | 
 | ||||||
|  | 	// Multi allows specifying a key capable of storing | ||||||
|  | 	// multiple results. Note this only supports invalidate. | ||||||
|  | 	Multi bool | ||||||
|  | 
 | ||||||
| 	// TODO: support toggling case sensitive lookups. | 	// TODO: support toggling case sensitive lookups. | ||||||
| 	// CaseSensitive bool | 	// CaseSensitive bool | ||||||
| } | } | ||||||
|  | @ -155,10 +159,14 @@ func (c *Cache[Value]) Load(lookup string, load func() (Value, error), keyParts | ||||||
| 	var ( | 	var ( | ||||||
| 		zero Value | 		zero Value | ||||||
| 		res  result[Value] | 		res  result[Value] | ||||||
|  | 		ok   bool | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// Get lookup key info by name. | 	// Get lookup key info by name. | ||||||
| 	keyInfo := c.lookups.get(lookup) | 	keyInfo := c.lookups.get(lookup) | ||||||
|  | 	if !keyInfo.unique { | ||||||
|  | 		panic("non-unique lookup does not support load: " + lookup) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Generate cache key string. | 	// Generate cache key string. | ||||||
| 	ckey := keyInfo.genKey(keyParts) | 	ckey := keyInfo.genKey(keyParts) | ||||||
|  | @ -167,11 +175,11 @@ func (c *Cache[Value]) Load(lookup string, load func() (Value, error), keyParts | ||||||
| 	c.cache.Lock() | 	c.cache.Lock() | ||||||
| 
 | 
 | ||||||
| 	// Look for primary cache key | 	// Look for primary cache key | ||||||
| 	pkey, ok := keyInfo.pkeys[ckey] | 	pkeys := keyInfo.pkeys[ckey] | ||||||
| 
 | 
 | ||||||
| 	if ok { | 	if ok = (len(pkeys) > 0); ok { | ||||||
| 		// Fetch the result for primary key | 		// Fetch the result for primary key | ||||||
| 		entry, _ := c.cache.Cache.Get(pkey) | 		entry, _ := c.cache.Cache.Get(pkeys[0]) | ||||||
| 		res = entry.Value | 		res = entry.Value | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -252,9 +260,13 @@ func (c *Cache[Value]) Store(value Value, store func() error) error { | ||||||
| // Has checks the cache for a positive result under the given lookup and key parts. | // Has checks the cache for a positive result under the given lookup and key parts. | ||||||
| func (c *Cache[Value]) Has(lookup string, keyParts ...any) bool { | func (c *Cache[Value]) Has(lookup string, keyParts ...any) bool { | ||||||
| 	var res result[Value] | 	var res result[Value] | ||||||
|  | 	var ok bool | ||||||
| 
 | 
 | ||||||
| 	// Get lookup key info by name. | 	// Get lookup key info by name. | ||||||
| 	keyInfo := c.lookups.get(lookup) | 	keyInfo := c.lookups.get(lookup) | ||||||
|  | 	if !keyInfo.unique { | ||||||
|  | 		panic("non-unique lookup does not support has: " + lookup) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Generate cache key string. | 	// Generate cache key string. | ||||||
| 	ckey := keyInfo.genKey(keyParts) | 	ckey := keyInfo.genKey(keyParts) | ||||||
|  | @ -263,11 +275,11 @@ func (c *Cache[Value]) Has(lookup string, keyParts ...any) bool { | ||||||
| 	c.cache.Lock() | 	c.cache.Lock() | ||||||
| 
 | 
 | ||||||
| 	// Look for primary key for cache key | 	// Look for primary key for cache key | ||||||
| 	pkey, ok := keyInfo.pkeys[ckey] | 	pkeys := keyInfo.pkeys[ckey] | ||||||
| 
 | 
 | ||||||
| 	if ok { | 	if ok = (len(pkeys) > 0); ok { | ||||||
| 		// Fetch the result for primary key | 		// Fetch the result for primary key | ||||||
| 		entry, _ := c.cache.Cache.Get(pkey) | 		entry, _ := c.cache.Cache.Get(pkeys[0]) | ||||||
| 		res = entry.Value | 		res = entry.Value | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -288,33 +300,35 @@ func (c *Cache[Value]) Invalidate(lookup string, keyParts ...any) { | ||||||
| 
 | 
 | ||||||
| 	// Look for primary key for cache key | 	// Look for primary key for cache key | ||||||
| 	c.cache.Lock() | 	c.cache.Lock() | ||||||
| 	pkey, ok := keyInfo.pkeys[ckey] | 	pkeys := keyInfo.pkeys[ckey] | ||||||
| 	c.cache.Unlock() | 	c.cache.Unlock() | ||||||
| 
 | 
 | ||||||
| 	if !ok { | 	for _, pkey := range pkeys { | ||||||
| 		return | 		// Invalidate each primary key | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Invalid by primary key |  | ||||||
| 		c.cache.Invalidate(pkey) | 		c.cache.Invalidate(pkey) | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // Clear empties the cache, calling the invalidate callback. | // Clear empties the cache, calling the invalidate callback. | ||||||
| func (c *Cache[Value]) Clear() { | func (c *Cache[Value]) Clear() { c.cache.Clear() } | ||||||
| 	c.cache.Clear() |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // store will cache this result under all of its required cache keys. | // store will cache this result under all of its required cache keys. | ||||||
| func (c *Cache[Value]) store(res result[Value]) { | func (c *Cache[Value]) store(res result[Value]) { | ||||||
|  | 	// Get primary key | ||||||
|  | 	pnext := c.next | ||||||
|  | 	c.next++ | ||||||
|  | 	if pnext > c.next { | ||||||
|  | 		panic("cache primary key overflow") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	for _, key := range res.Keys { | 	for _, key := range res.Keys { | ||||||
| 		pkeys := key.info.pkeys | 		// Look for cache primary keys. | ||||||
|  | 		pkeys := key.info.pkeys[key.key] | ||||||
| 
 | 
 | ||||||
| 		// Look for cache primary key | 		if key.info.unique && len(pkeys) > 0 { | ||||||
| 		pkey, ok := pkeys[key.key] | 			for _, conflict := range pkeys { | ||||||
| 
 |  | ||||||
| 		if ok { |  | ||||||
| 				// Get the overlapping result with this key. | 				// Get the overlapping result with this key. | ||||||
| 			entry, _ := c.cache.Cache.Get(pkey) | 				entry, _ := c.cache.Cache.Get(conflict) | ||||||
| 
 | 
 | ||||||
| 				// From conflicting entry, drop this key, this | 				// From conflicting entry, drop this key, this | ||||||
| 				// will prevent eviction cleanup key confusion. | 				// will prevent eviction cleanup key confusion. | ||||||
|  | @ -323,28 +337,23 @@ func (c *Cache[Value]) store(res result[Value]) { | ||||||
| 				if len(entry.Value.Keys) == 0 { | 				if len(entry.Value.Keys) == 0 { | ||||||
| 					// We just over-wrote the only lookup key for | 					// We just over-wrote the only lookup key for | ||||||
| 					// this value, so we drop its primary key too. | 					// this value, so we drop its primary key too. | ||||||
| 				c.cache.Cache.Delete(pkey) | 					c.cache.Cache.Delete(conflict) | ||||||
| 			} |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	// Get primary key | 			// Drop these keys. | ||||||
| 	pkey := c.next | 			pkeys = pkeys[:0] | ||||||
| 	c.next++ |  | ||||||
| 	if pkey > c.next { |  | ||||||
| 		panic("cache primary key overflow") |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	// Store all primary key lookups | 		// Store primary key lookup. | ||||||
| 	for _, key := range res.Keys { | 		pkeys = append(pkeys, pnext) | ||||||
| 		pkeys := key.info.pkeys | 		key.info.pkeys[key.key] = pkeys | ||||||
| 		pkeys[key.key] = pkey |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Store main entry under primary key, using evict hook if needed | 	// Store main entry under primary key, using evict hook if needed | ||||||
| 	c.cache.Cache.SetWithHook(pkey, &ttl.Entry[int64, result[Value]]{ | 	c.cache.Cache.SetWithHook(pnext, &ttl.Entry[int64, result[Value]]{ | ||||||
| 		Expiry: time.Now().Add(c.cache.TTL), | 		Expiry: time.Now().Add(c.cache.TTL), | ||||||
| 		Key:    pkey, | 		Key:    pnext, | ||||||
| 		Value:  res, | 		Value:  res, | ||||||
| 	}, func(_ int64, item *ttl.Entry[int64, result[Value]]) { | 	}, func(_ int64, item *ttl.Entry[int64, result[Value]]) { | ||||||
| 		c.cache.Evict(item) | 		c.cache.Evict(item) | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								vendor/codeberg.org/gruf/go-cache/v3/result/key.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/codeberg.org/gruf/go-cache/v3/result/key.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -122,14 +122,17 @@ type structKey struct { | ||||||
| 	//      zero != "" --> don't allow zero value keys | 	//      zero != "" --> don't allow zero value keys | ||||||
| 	zero string | 	zero string | ||||||
| 
 | 
 | ||||||
|  | 	// unique determines whether this structKey supports | ||||||
|  | 	// multiple or just the singular unique result. | ||||||
|  | 	unique bool | ||||||
|  | 
 | ||||||
| 	// fields is a slice of runtime struct field | 	// fields is a slice of runtime struct field | ||||||
| 	// indices, of the fields encompassed by this key. | 	// indices, of the fields encompassed by this key. | ||||||
| 
 |  | ||||||
| 	fields []structField | 	fields []structField | ||||||
| 
 | 
 | ||||||
| 	// pkeys is a lookup of stored struct key values | 	// pkeys is a lookup of stored struct key values | ||||||
| 	// to the primary cache lookup key (int64). | 	// to the primary cache lookup key (int64). | ||||||
| 	pkeys map[string]int64 | 	pkeys map[string][]int64 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type structField struct { | type structField struct { | ||||||
|  | @ -220,8 +223,11 @@ func newStructKey(lk Lookup, t reflect.Type) structKey { | ||||||
| 		sk.zero = sk.genKey(zeros) | 		sk.zero = sk.genKey(zeros) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Set unique lookup flag. | ||||||
|  | 	sk.unique = !lk.Multi | ||||||
|  | 
 | ||||||
| 	// Allocate primary lookup map | 	// Allocate primary lookup map | ||||||
| 	sk.pkeys = make(map[string]int64) | 	sk.pkeys = make(map[string][]int64) | ||||||
| 
 | 
 | ||||||
| 	return sk | 	return sk | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -13,7 +13,7 @@ codeberg.org/gruf/go-bytesize | ||||||
| # codeberg.org/gruf/go-byteutil v1.1.2 | # codeberg.org/gruf/go-byteutil v1.1.2 | ||||||
| ## explicit; go 1.16 | ## explicit; go 1.16 | ||||||
| codeberg.org/gruf/go-byteutil | codeberg.org/gruf/go-byteutil | ||||||
| # codeberg.org/gruf/go-cache/v3 v3.2.6 | # codeberg.org/gruf/go-cache/v3 v3.3.0 | ||||||
| ## explicit; go 1.19 | ## explicit; go 1.19 | ||||||
| codeberg.org/gruf/go-cache/v3 | codeberg.org/gruf/go-cache/v3 | ||||||
| codeberg.org/gruf/go-cache/v3/result | codeberg.org/gruf/go-cache/v3/result | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue