Skip to content

Allow opt-in shared template cache scope for cross-team layer dedup (single-trust-domain deployments) #3748

Open

Description

@AdaAibaby

Summary

Layer caching is content-addressed (HashKeys = sha256 over base + keys, no team), but the storage path is prefixed by cacheScope, and the API hardcodes cacheScope = teamID. So two teams that build byte-identical layers each get a private copy of (a) the layer index + tar in the build-cache bucket and (b) the buildID-keyed memfile/rootfs in the template bucket. In a single-trust-domain / self-hosted deployment this is pure waste — the hash already proves the content is identical.

Where

  • packages/api/internal/template-manager/create_template.go — CacheScope: new(teamID.String())
  • packages/api/internal/template-manager/upload_template_layer_files.go — CacheScope: new(teamID.String())
  • packages/orchestrator/pkg/template/build/storage/paths/paths.go — path.Join(cacheScope, saveType, file)
  • packages/orchestrator/pkg/template/build/storage/cache/cache.go — HashKeys is pure content hash; the index maps hash -> buildID, and that buildID keys the (unscoped) memfile/rootfs. So sharing the scope dedups the large artifacts too, transitively.

Proposal

Make the cache scope optionally shared, opt-in, default unchanged. e.g. an env/flag (TEMPLATE_CACHE_SCOPE) read by the API: when set, use it as the scope for every team; when unset, fall back to teamID (today behavior). Orchestrator needs no change — it already honors TemplateCreateRequest.CacheScope / InitLayerFileUploadRequest.CacheScope.

Why opt-in / default-off

A shared scope removes per-team cache isolation: any team can hit (and thus prove the existence of, and reuse) another team's cached layers. That is unacceptable for multi-tenant E2B Cloud but fine for a single organization. Hence default must stay teamID; sharing is an operator opt-in.

Relation to #3742 (template-gc)

Orthogonal. #3742 reclaims unreferenced {buildID}/ prefixes by liveness. Cross-team duplicates are each still referenced by their own team's template, so GC keeps both — it never merges duplicates. Scope-sharing is what collapses future duplicates to one copy; #3742 is what later reclaims the old copies once they age out of reference. They compose; neither replaces the other.

Happy to send a PR along these lines if the approach looks acceptable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions