Use otter for site cache
This commit is contained in:
+87
-154
@@ -3,29 +3,39 @@ package cachedrepo
|
|||||||
import (
|
import (
|
||||||
"quay/app/models"
|
"quay/app/models"
|
||||||
"quay/app/repository"
|
"quay/app/repository"
|
||||||
"sync"
|
|
||||||
|
"github.com/maypok86/otter/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CachedSiteRepository struct {
|
type CachedSiteRepository struct {
|
||||||
inner repository.SiteRepository
|
inner repository.SiteRepository
|
||||||
mu sync.RWMutex
|
sites *otter.Cache[string, *models.Site]
|
||||||
|
list *otter.Cache[string, []models.Site]
|
||||||
sites map[string]*models.Site // id -> site
|
forwardRules *otter.Cache[string, *models.ForwardRule]
|
||||||
siteList []models.Site // cached ListSites result
|
customHeaders *otter.Cache[string, *models.CustomHeaders]
|
||||||
siteListValid bool
|
headers *otter.Cache[string, *models.Header]
|
||||||
|
|
||||||
forwardRules map[string]*models.ForwardRule // id -> rule
|
|
||||||
customHeaders map[string]*models.CustomHeaders // id -> custom headers
|
|
||||||
headers map[string]*models.Header // id -> header
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const siteListKey = "__list__"
|
||||||
|
|
||||||
func NewCachedSiteRepository(inner repository.SiteRepository) *CachedSiteRepository {
|
func NewCachedSiteRepository(inner repository.SiteRepository) *CachedSiteRepository {
|
||||||
return &CachedSiteRepository{
|
return &CachedSiteRepository{
|
||||||
inner: inner,
|
inner: inner,
|
||||||
sites: make(map[string]*models.Site),
|
sites: otter.Must(&otter.Options[string, *models.Site]{
|
||||||
forwardRules: make(map[string]*models.ForwardRule),
|
MaximumSize: 1_000,
|
||||||
customHeaders: make(map[string]*models.CustomHeaders),
|
}),
|
||||||
headers: make(map[string]*models.Header),
|
list: otter.Must(&otter.Options[string, []models.Site]{
|
||||||
|
MaximumSize: 1,
|
||||||
|
}),
|
||||||
|
forwardRules: otter.Must(&otter.Options[string, *models.ForwardRule]{
|
||||||
|
MaximumSize: 10_000,
|
||||||
|
}),
|
||||||
|
customHeaders: otter.Must(&otter.Options[string, *models.CustomHeaders]{
|
||||||
|
MaximumSize: 10_000,
|
||||||
|
}),
|
||||||
|
headers: otter.Must(&otter.Options[string, *models.Header]{
|
||||||
|
MaximumSize: 10_000,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,66 +44,41 @@ var _ repository.SiteRepository = (*CachedSiteRepository)(nil)
|
|||||||
// Sites
|
// Sites
|
||||||
|
|
||||||
func (c *CachedSiteRepository) GetSite(id string) (*models.Site, error) {
|
func (c *CachedSiteRepository) GetSite(id string) (*models.Site, error) {
|
||||||
c.mu.RLock()
|
if s, ok := c.sites.GetIfPresent(id); ok {
|
||||||
if s, ok := c.sites[id]; ok {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
s, err := c.inner.GetSite(id)
|
s, err := c.inner.GetSite(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.sites.Set(id, s)
|
||||||
c.mu.Lock()
|
|
||||||
c.sites[id] = s
|
|
||||||
c.mu.Unlock()
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachedSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) {
|
func (c *CachedSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) {
|
||||||
c.mu.RLock()
|
if s, ok := c.sites.GetIfPresent(domain); ok {
|
||||||
if s, ok := c.sites[domain]; ok {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
s, err := c.inner.GetSiteByDomain(domain)
|
s, err := c.inner.GetSiteByDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.sites.Set(domain, s)
|
||||||
c.mu.Lock()
|
|
||||||
c.sites[domain] = s
|
|
||||||
c.mu.Unlock()
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachedSiteRepository) ListSites() ([]models.Site, error) {
|
func (c *CachedSiteRepository) ListSites() ([]models.Site, error) {
|
||||||
c.mu.RLock()
|
if sites, ok := c.list.GetIfPresent(siteListKey); ok {
|
||||||
if c.siteListValid {
|
return sites, nil
|
||||||
cp := make([]models.Site, len(c.siteList))
|
|
||||||
copy(cp, c.siteList)
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return cp, nil
|
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
sites, err := c.inner.ListSites()
|
sites, err := c.inner.ListSites()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.list.Set(siteListKey, sites)
|
||||||
c.mu.Lock()
|
|
||||||
c.siteList = sites
|
|
||||||
c.siteListValid = true
|
|
||||||
for i := range sites {
|
for i := range sites {
|
||||||
s := sites[i]
|
c.sites.Set(sites[i].ID, &sites[i])
|
||||||
c.sites[s.ID] = &s
|
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
|
||||||
return sites, nil
|
return sites, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +86,8 @@ func (c *CachedSiteRepository) CreateSite(s *models.Site) error {
|
|||||||
if err := c.inner.CreateSite(s); err != nil {
|
if err := c.inner.CreateSite(s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.sites.Set(s.ID, s)
|
||||||
c.sites[s.ID] = s
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,54 +95,41 @@ func (c *CachedSiteRepository) UpdateSite(s *models.Site) error {
|
|||||||
if err := c.inner.UpdateSite(s); err != nil {
|
if err := c.inner.UpdateSite(s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.sites.Set(s.ID, s)
|
||||||
c.sites[s.ID] = s
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachedSiteRepository) ToggleEnabled(id string) (enabledReturn bool, err error) {
|
func (c *CachedSiteRepository) ToggleEnabled(id string) (bool, error) {
|
||||||
enabledReturn, err = c.inner.ToggleEnabled(id)
|
enabled, err := c.inner.ToggleEnabled(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.sites.Invalidate(id)
|
||||||
delete(c.sites, id)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
return enabled, nil
|
||||||
c.mu.Unlock()
|
|
||||||
return enabledReturn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CachedSiteRepository) DeleteSite(id string) error {
|
func (c *CachedSiteRepository) DeleteSite(id string) error {
|
||||||
if err := c.inner.DeleteSite(id); err != nil {
|
if err := c.inner.DeleteSite(id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.sites.Invalidate(id)
|
||||||
delete(c.sites, id)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward Rules
|
// Forward Rules
|
||||||
|
|
||||||
func (c *CachedSiteRepository) GetForwardRule(id string) (*models.ForwardRule, error) {
|
func (c *CachedSiteRepository) GetForwardRule(id string) (*models.ForwardRule, error) {
|
||||||
c.mu.RLock()
|
if fr, ok := c.forwardRules.GetIfPresent(id); ok {
|
||||||
if fr, ok := c.forwardRules[id]; ok {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return fr, nil
|
return fr, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
fr, err := c.inner.GetForwardRule(id)
|
fr, err := c.inner.GetForwardRule(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.forwardRules.Set(id, fr)
|
||||||
c.mu.Lock()
|
|
||||||
c.forwardRules[id] = fr
|
|
||||||
c.mu.Unlock()
|
|
||||||
return fr, nil
|
return fr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,11 +137,9 @@ func (c *CachedSiteRepository) CreateForwardRule(siteID string, fr *models.Forwa
|
|||||||
if err := c.inner.CreateForwardRule(siteID, fr); err != nil {
|
if err := c.inner.CreateForwardRule(siteID, fr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.forwardRules.Set(fr.ID, fr)
|
||||||
c.forwardRules[fr.ID] = fr
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID) // site's embedded rules are now stale
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,11 +147,9 @@ func (c *CachedSiteRepository) UpdateForwardRule(siteID string, fr *models.Forwa
|
|||||||
if err := c.inner.UpdateForwardRule(siteID, fr); err != nil {
|
if err := c.inner.UpdateForwardRule(siteID, fr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.forwardRules.Set(fr.ID, fr)
|
||||||
c.forwardRules[fr.ID] = fr
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,32 +157,23 @@ func (c *CachedSiteRepository) DeleteForwardRule(siteID string, id string) error
|
|||||||
if err := c.inner.DeleteForwardRule(siteID, id); err != nil {
|
if err := c.inner.DeleteForwardRule(siteID, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.forwardRules.Invalidate(id)
|
||||||
delete(c.forwardRules, id)
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom Headers
|
// Custom Headers
|
||||||
|
|
||||||
func (c *CachedSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) {
|
func (c *CachedSiteRepository) GetCustomHeaders(id string) (*models.CustomHeaders, error) {
|
||||||
c.mu.RLock()
|
if ch, ok := c.customHeaders.GetIfPresent(id); ok {
|
||||||
if ch, ok := c.customHeaders[id]; ok {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return ch, nil
|
return ch, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
ch, err := c.inner.GetCustomHeaders(id)
|
ch, err := c.inner.GetCustomHeaders(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.customHeaders.Set(id, ch)
|
||||||
c.mu.Lock()
|
|
||||||
c.customHeaders[id] = ch
|
|
||||||
c.mu.Unlock()
|
|
||||||
return ch, nil
|
return ch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,11 +181,9 @@ func (c *CachedSiteRepository) CreateCustomHeaders(siteID string, ch *models.Cus
|
|||||||
if err := c.inner.CreateCustomHeaders(siteID, ch); err != nil {
|
if err := c.inner.CreateCustomHeaders(siteID, ch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.customHeaders.Set(ch.ID, ch)
|
||||||
c.customHeaders[ch.ID] = ch
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,11 +191,9 @@ func (c *CachedSiteRepository) UpdateCustomHeaders(siteID string, ch *models.Cus
|
|||||||
if err := c.inner.UpdateCustomHeaders(siteID, ch); err != nil {
|
if err := c.inner.UpdateCustomHeaders(siteID, ch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.customHeaders.Set(ch.ID, ch)
|
||||||
c.customHeaders[ch.ID] = ch
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,32 +201,23 @@ func (c *CachedSiteRepository) DeleteCustomHeaders(siteID string, id string) err
|
|||||||
if err := c.inner.DeleteCustomHeaders(siteID, id); err != nil {
|
if err := c.inner.DeleteCustomHeaders(siteID, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.customHeaders.Invalidate(id)
|
||||||
delete(c.customHeaders, id)
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
|
|
||||||
func (c *CachedSiteRepository) GetHeader(id string) (*models.Header, error) {
|
func (c *CachedSiteRepository) GetHeader(id string) (*models.Header, error) {
|
||||||
c.mu.RLock()
|
if h, ok := c.headers.GetIfPresent(id); ok {
|
||||||
if h, ok := c.headers[id]; ok {
|
|
||||||
c.mu.RUnlock()
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
c.mu.RUnlock()
|
|
||||||
|
|
||||||
h, err := c.inner.GetHeader(id)
|
h, err := c.inner.GetHeader(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
c.headers.Set(id, h)
|
||||||
c.mu.Lock()
|
|
||||||
c.headers[id] = h
|
|
||||||
c.mu.Unlock()
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,12 +225,10 @@ func (c *CachedSiteRepository) CreateHeader(siteID string, customHeaderID string
|
|||||||
if err := c.inner.CreateHeader(siteID, customHeaderID, h); err != nil {
|
if err := c.inner.CreateHeader(siteID, customHeaderID, h); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.headers.Set(h.ID, h)
|
||||||
c.headers[h.ID] = h
|
c.customHeaders.Invalidate(customHeaderID)
|
||||||
delete(c.customHeaders, customHeaderID)
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,11 +236,9 @@ func (c *CachedSiteRepository) UpdateHeader(siteID string, h *models.Header) err
|
|||||||
if err := c.inner.UpdateHeader(siteID, h); err != nil {
|
if err := c.inner.UpdateHeader(siteID, h); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.headers.Set(h.ID, h)
|
||||||
c.headers[h.ID] = h
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,30 +246,23 @@ func (c *CachedSiteRepository) DeleteHeader(siteID string, id string) error {
|
|||||||
if err := c.inner.DeleteHeader(siteID, id); err != nil {
|
if err := c.inner.DeleteHeader(siteID, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.mu.Lock()
|
c.headers.Invalidate(id)
|
||||||
delete(c.headers, id)
|
c.sites.Invalidate(siteID)
|
||||||
delete(c.sites, siteID)
|
c.list.Invalidate(siteListKey)
|
||||||
c.siteListValid = false
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate explicitly evicts all cached data
|
// Invalidate explicitly evicts all cached data.
|
||||||
func (c *CachedSiteRepository) Invalidate() {
|
func (c *CachedSiteRepository) Invalidate() {
|
||||||
c.mu.Lock()
|
c.sites.InvalidateAll()
|
||||||
defer c.mu.Unlock()
|
c.list.InvalidateAll()
|
||||||
c.sites = make(map[string]*models.Site)
|
c.forwardRules.InvalidateAll()
|
||||||
c.siteList = nil
|
c.customHeaders.InvalidateAll()
|
||||||
c.siteListValid = false
|
c.headers.InvalidateAll()
|
||||||
c.forwardRules = make(map[string]*models.ForwardRule)
|
|
||||||
c.customHeaders = make(map[string]*models.CustomHeaders)
|
|
||||||
c.headers = make(map[string]*models.Header)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvalidateSite evicts a single site and marks the list stale.
|
// InvalidateSite evicts a single site and marks the list stale.
|
||||||
func (c *CachedSiteRepository) InvalidateSite(id string) {
|
func (c *CachedSiteRepository) InvalidateSite(id string) {
|
||||||
c.mu.Lock()
|
c.sites.Invalidate(id)
|
||||||
defer c.mu.Unlock()
|
c.list.Invalidate(siteListKey)
|
||||||
delete(c.sites, id)
|
|
||||||
c.siteListValid = false
|
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"quay/app/cachedrepo"
|
|
||||||
"quay/app/handlers"
|
"quay/app/handlers"
|
||||||
"quay/app/middleware"
|
"quay/app/middleware"
|
||||||
"quay/app/models"
|
"quay/app/models"
|
||||||
@@ -20,7 +19,7 @@ const BootstrapUserUsername = "admin"
|
|||||||
const BootstrapUserPassword = "admin"
|
const BootstrapUserPassword = "admin"
|
||||||
|
|
||||||
func Register(app *fiber.App, cfg *config.Config, envCfg *envconfig.EnvConfig, db *sql.DB) {
|
func Register(app *fiber.App, cfg *config.Config, envCfg *envconfig.EnvConfig, db *sql.DB) {
|
||||||
siteRepository := cachedrepo.NewCachedSiteRepository(database.NewSQLiteSiteRepository(db))
|
siteRepository := database.NewSQLiteSiteRepository(db)
|
||||||
deploymentRepository := database.NewSQLiteDeploymentRepository(db)
|
deploymentRepository := database.NewSQLiteDeploymentRepository(db)
|
||||||
userRepository := database.NewSQLiteUserRepository(db)
|
userRepository := database.NewSQLiteUserRepository(db)
|
||||||
gitServerRepository := database.NewSQLiteGitServerRepository(db)
|
gitServerRepository := database.NewSQLiteGitServerRepository(db)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ require (
|
|||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||||
github.com/go-openapi/spec v0.20.4 // indirect
|
github.com/go-openapi/spec v0.20.4 // indirect
|
||||||
@@ -30,7 +31,10 @@ require (
|
|||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/mailru/easyjson v0.7.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/maypok86/otter/v2 v2.3.0 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
github.com/tinylib/msgp v1.6.3 // indirect
|
github.com/tinylib/msgp v1.6.3 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.69.0 // indirect
|
github.com/valyala/fasthttp v1.69.0 // indirect
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmITgb4=
|
github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmITgb4=
|
||||||
github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/maypok86/otter/v2 v2.3.0 h1:8H8AVVFUSzJwIegKwv1uF5aGitTY+AIrtktg7OcLs8w=
|
||||||
|
github.com/maypok86/otter/v2 v2.3.0/go.mod h1:XgIdlpmL6jYz882/CAx1E4C1ukfgDKSaw4mWq59+7l8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
|
|||||||
Reference in New Issue
Block a user