From 9af55656596c823ed5d8cc006dd909564d720e6f Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 19 May 2026 12:14:15 +0000 Subject: [PATCH] Make the existence cache more type-safe. This makes the uncertain nature of a Bloom filter hit explicit at every call site. --- src/caddy.go | 2 +- src/existence.go | 62 +++++++++++++++++++++++++++++++++++------------- src/pages.go | 5 ++-- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/caddy.go b/src/caddy.go index 888fab5..c6640a6 100644 --- a/src/caddy.go +++ b/src/caddy.go @@ -30,7 +30,7 @@ func ServeCaddy(w http.ResponseWriter, r *http.Request) { domain = strings.ToLower(domain) // Run a cheap check as to whether we might be serving the domain. - var found = existenceCache.CheckDomain(r.Context(), domain) + found := existenceCache.CheckDomain(r.Context(), domain).IsPossible() if found { // Run an expensive check as to whether we are actually serving the domain. diff --git a/src/existence.go b/src/existence.go index b331808..61e4d73 100644 --- a/src/existence.go +++ b/src/existence.go @@ -16,12 +16,38 @@ import ( "github.com/bits-and-blooms/bloom/v3" ) +type Existence int + +const ( + ExistenceImpossible Existence = iota + ExistencePossible +) + +func (value Existence) IsImpossible() bool { + return value == ExistenceImpossible +} + +func (value Existence) IsPossible() bool { + return value == ExistencePossible +} + +func (value Existence) String() string { + switch value { + case ExistenceImpossible: + return "impossible" + case ExistencePossible: + return "possible" + default: + return "(invalid)" + } +} + type ExistenceCache interface { // Check if we might be serving the site. - CheckSite(ctx context.Context, site string) (found bool) + CheckSite(ctx context.Context, site string) (result Existence) // Check if we might be serving the domain. - CheckDomain(ctx context.Context, domain string) (found bool) + CheckDomain(ctx context.Context, domain string) (result Existence) // Add the site to the cache. AddSite(ctx context.Context, site string) @@ -118,38 +144,38 @@ func (c *bloomExistenceCache) refresh(ctx context.Context) error { return nil } -func (c *bloomExistenceCache) CheckSite(ctx context.Context, site string) (found bool) { +func (c *bloomExistenceCache) CheckSite(ctx context.Context, site string) (result Existence) { select { case c.accessCh <- struct{}{}: default: } c.filterMu.Lock() - found = c.sites.TestString(site) + if c.sites.TestString(site) { + result = ExistencePossible + } else { + result = ExistenceImpossible + } c.filterMu.Unlock() - result := "miss" - if found { - result = "hit" - } logc.Printf(ctx, "existence: site %s: %s", site, result) return } -func (c *bloomExistenceCache) CheckDomain(ctx context.Context, domain string) (found bool) { +func (c *bloomExistenceCache) CheckDomain(ctx context.Context, domain string) (result Existence) { select { case c.accessCh <- struct{}{}: default: } c.filterMu.Lock() - found = c.domains.TestString(domain) + if c.domains.TestString(domain) { + result = ExistencePossible + } else { + result = ExistenceImpossible + } c.filterMu.Unlock() - result := "miss" - if found { - result = "hit" - } logc.Printf(ctx, "existence: domain %s: %s", domain, result) return } @@ -170,8 +196,12 @@ func (c *bloomExistenceCache) AddSite(ctx context.Context, site string) { type dummyExistenceCache struct{} -func (d dummyExistenceCache) CheckSite(context.Context, string) bool { return true } +func (d dummyExistenceCache) CheckSite(context.Context, string) Existence { + return ExistencePossible +} -func (d dummyExistenceCache) CheckDomain(context.Context, string) bool { return true } +func (d dummyExistenceCache) CheckDomain(context.Context, string) Existence { + return ExistencePossible +} func (d dummyExistenceCache) AddSite(context.Context, string) {} diff --git a/src/pages.go b/src/pages.go index 7f23490..b98b82d 100644 --- a/src/pages.go +++ b/src/pages.go @@ -139,7 +139,7 @@ func getPage(w http.ResponseWriter, r *http.Request) error { indexManifestCh := make(chan indexManifestResult, 1) go func() { webRoot := makeWebRoot(host, ".index") - if !existenceCache.CheckSite(r.Context(), webRoot) { + if existenceCache.CheckSite(r.Context(), webRoot).IsImpossible() { close(indexManifestCh) return } @@ -154,7 +154,8 @@ func getPage(w http.ResponseWriter, r *http.Request) error { sitePath = strings.TrimPrefix(r.URL.Path, "/") if projectName, projectPath, hasProjectSlash := strings.Cut(sitePath, "/"); projectName != "" { webRoot := makeWebRoot(host, projectName) - if ValidateProjectName(projectName) == nil && existenceCache.CheckSite(r.Context(), webRoot) { + if ValidateProjectName(projectName) == nil && + existenceCache.CheckSite(r.Context(), webRoot).IsPossible() { var projectManifest *Manifest var projectMetadata ManifestMetadata projectManifest, projectMetadata, err = backend.GetManifest(