@@ -2,10 +2,10 @@ package templatecache
22
33import (
44 "context"
5- "database/sql"
65 "errors"
76 "fmt"
87 "net/http"
8+ "strings"
99 "time"
1010
1111 "github.com/google/uuid"
@@ -14,8 +14,10 @@ import (
1414 "github.com/e2b-dev/infra/packages/api/internal/api"
1515 "github.com/e2b-dev/infra/packages/api/internal/utils"
1616 sqlcdb "github.com/e2b-dev/infra/packages/db/client"
17+ "github.com/e2b-dev/infra/packages/db/dberrors"
1718 "github.com/e2b-dev/infra/packages/db/queries"
1819 "github.com/e2b-dev/infra/packages/shared/pkg/cache"
20+ "github.com/e2b-dev/infra/packages/shared/pkg/id"
1921)
2022
2123const (
@@ -29,6 +31,7 @@ type TemplateInfo struct {
2931 teamID uuid.UUID
3032 clusterID uuid.UUID
3133 build * queries.EnvBuild
34+ tag * string
3235}
3336
3437type AliasCache struct {
@@ -72,7 +75,7 @@ func NewTemplateCache(db *sqlcdb.Client) *TemplateCache {
7275 RefreshTimeout : refreshTimeout ,
7376 // With this we can use alias for getting template info without having it as a key in the cache
7477 ExtractKeyFunc : func (value * TemplateInfo ) string {
75- return value .template .TemplateID
78+ return buildCacheKey ( value .template .TemplateID , value . tag )
7679 },
7780 }
7881 aliasCache := NewAliasCache ()
@@ -84,15 +87,25 @@ func NewTemplateCache(db *sqlcdb.Client) *TemplateCache {
8487 }
8588}
8689
87- func (c * TemplateCache ) Get (ctx context.Context , aliasOrEnvID string , teamID uuid.UUID , clusterID uuid.UUID , public bool ) (* api.Template , * queries.EnvBuild , * api.APIError ) {
90+ func buildCacheKey (templateID string , tag * string ) string {
91+ if tag == nil {
92+ return templateID + ":" + id .DefaultTag
93+ }
94+
95+ return templateID + ":" + * tag
96+ }
97+
98+ func (c * TemplateCache ) Get (ctx context.Context , aliasOrEnvID string , tag * string , teamID uuid.UUID , clusterID uuid.UUID , public bool ) (* api.Template , * queries.EnvBuild , * api.APIError ) {
8899 // Resolve alias to template ID if needed
89100 templateID , found := c .aliasCache .Get (aliasOrEnvID )
90101 if ! found {
91102 templateID = aliasOrEnvID
92103 }
93104
105+ cacheKey := buildCacheKey (templateID , tag )
106+
94107 // Fetch or get from cache with automatic refresh
95- templateInfo , err := c .cache .GetOrSet (ctx , templateID , c .fetchTemplateInfo )
108+ templateInfo , err := c .cache .GetOrSet (ctx , cacheKey , c .fetchTemplateInfo )
96109 if err != nil {
97110 var apiErr * api.APIError
98111 if errors .As (err , & apiErr ) {
@@ -115,11 +128,24 @@ func (c *TemplateCache) Get(ctx context.Context, aliasOrEnvID string, teamID uui
115128}
116129
117130// fetchTemplateInfo fetches template info from the database
118- func (c * TemplateCache ) fetchTemplateInfo (ctx context.Context , aliasOrEnvID string ) (* TemplateInfo , error ) {
119- result , err := c . db . GetTemplateWithBuild ( ctx , aliasOrEnvID )
131+ func (c * TemplateCache ) fetchTemplateInfo (ctx context.Context , cacheKey string ) (* TemplateInfo , error ) {
132+ aliasOrEnvID , tag , err := id . ParseTemplateIDOrAliasWithTag ( cacheKey )
120133 if err != nil {
121- if errors .Is (err , sql .ErrNoRows ) {
122- return nil , & api.APIError {Code : http .StatusNotFound , ClientMsg : fmt .Sprintf ("template '%s' not found" , aliasOrEnvID ), Err : err }
134+ return nil , & api.APIError {Code : http .StatusBadRequest , ClientMsg : fmt .Sprintf ("invalid template ID: %s" , err ), Err : err }
135+ }
136+
137+ result , err := c .db .GetTemplateWithBuildByTag (ctx , queries.GetTemplateWithBuildByTagParams {
138+ AliasOrEnvID : aliasOrEnvID ,
139+ Tag : tag ,
140+ })
141+ if err != nil {
142+ if dberrors .IsNotFoundError (err ) {
143+ tagMsg := ""
144+ if tag != nil {
145+ tagMsg = fmt .Sprintf (" with tag '%s'" , * tag )
146+ }
147+
148+ return nil , & api.APIError {Code : http .StatusNotFound , ClientMsg : fmt .Sprintf ("template '%s'%s not found" , aliasOrEnvID , tagMsg ), Err : err }
123149 }
124150
125151 return nil , & api.APIError {Code : http .StatusInternalServerError , ClientMsg : fmt .Sprintf ("error while getting template: %v" , err ), Err : err }
@@ -129,7 +155,7 @@ func (c *TemplateCache) fetchTemplateInfo(ctx context.Context, aliasOrEnvID stri
129155 template := result .Env
130156 clusterID := utils .WithClusterFallback (template .ClusterID )
131157
132- // Update alias cache
158+ // Update alias cache (without tag, as aliases map to template IDs)
133159 c .aliasCache .Set (template .ID , template .ID )
134160 for _ , alias := range result .Aliases {
135161 c .aliasCache .Set (alias , template .ID )
@@ -145,12 +171,22 @@ func (c *TemplateCache) fetchTemplateInfo(ctx context.Context, aliasOrEnvID stri
145171 teamID : template .TeamID ,
146172 clusterID : clusterID ,
147173 build : build ,
174+ tag : tag ,
148175 }, nil
149176}
150177
151- // Invalidate invalidates the cache for the given templateID
152- func (c * TemplateCache ) Invalidate (templateID string ) {
153- c .cache .Delete (templateID )
178+ func (c * TemplateCache ) Invalidate (templateID string , tag * string ) {
179+ c .cache .Delete (buildCacheKey (templateID , tag ))
180+ }
181+
182+ // Invalidate invalidates the cache for the given templateID across all tags
183+ func (c * TemplateCache ) InvalidateAllTags (templateID string ) {
184+ templateIDKey := templateID + ":"
185+ for _ , key := range c .cache .Keys () {
186+ if strings .HasPrefix (key , templateIDKey ) {
187+ c .cache .Delete (key )
188+ }
189+ }
154190}
155191
156192func (c * TemplateCache ) Close (ctx context.Context ) error {
0 commit comments