Skip to content

Commit 6fc5846

Browse files
kalyazinclaude
andcommitted
feat(orch): add memfd identity-offset source for provisional-header serving
Add DedupedMemfdCache.ServeMemfd (identity device-offset reads guarded by the existing memfd RWMutex) and MemfdIdentitySource, a local diff source that serves dirty pages from the still-mapped memfd while dedup runs. This is what a provisional (pre-dedup) memfile header reads from, so a resume overlapping the pause can serve dirty pages immediately instead of waiting for the deduped header. Publish the memfd at construction and free it exactly once under the write lock (releaseMemfd) so serving can't race the drain's memfd close. Composes with the in-flight drain serving rather than replacing it. Not yet wired into the pause/resume path. Signed-off-by: Nikita Kalyazin <nikita.kalyazin@e2b.dev> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 19335be commit 6fc5846

1 file changed

Lines changed: 86 additions & 6 deletions

File tree

  • packages/orchestrator/pkg/sandbox/block

packages/orchestrator/pkg/sandbox/block/memfd.go

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,11 @@ func NewCacheFromMemfdDeduped(
275275
cancel: cancel,
276276
done: utils.NewSetOnce[*Cache](),
277277
inflight: inflightServe,
278+
// Publish the memfd up front (guarded by mu) so a provisional-header
279+
// resume can serve dirty pages via ServeMemfd during the compare window,
280+
// before metaOut/the deduped header resolves. runDedup frees it under mu
281+
// after the drain.
282+
memfd: memfd,
278283
}
279284
go d.runDedup(drainCtx, base, blockSize, memfd, dirty, bestEffort, directIO, budget, inputEmpty, metaOut)
280285

@@ -353,7 +358,7 @@ func (d *DedupedMemfdCache) runDedup(
353358
compareDur := time.Since(compareStart)
354359
if err != nil {
355360
logSetOnceErr(ctx, "dedup metaOut", metaOut.SetError(err))
356-
logSetOnceErr(ctx, "dedup done", d.done.SetError(errors.Join(err, memfd.Close())))
361+
logSetOnceErr(ctx, "dedup done", d.done.SetError(errors.Join(err, d.releaseMemfd())))
357362

358363
return
359364
}
@@ -388,11 +393,7 @@ func (d *DedupedMemfdCache) runDedup(
388393
writeStart := time.Now()
389394
cache, err := dedupDrain(ctx, src, plan.pageDirty, blockSize, d.outPath, directIO)
390395
writeDur := time.Since(writeStart)
391-
d.mu.Lock()
392-
closeErr := memfd.Close()
393-
d.memfd = nil
394-
d.mu.Unlock()
395-
if closeErr != nil {
396+
if closeErr := d.releaseMemfd(); closeErr != nil {
396397
logger.L().Warn(ctx, "close memfd after dedup drain", zap.Error(closeErr))
397398
}
398399

@@ -475,6 +476,40 @@ func (d *DedupedMemfdCache) Slice(off, length int64) ([]byte, error) {
475476
return c.Slice(off, length)
476477
}
477478

479+
// releaseMemfd closes the memfd exactly once, under the write lock so it can't
480+
// be unmapped beneath an in-flight ServeMemfd/tryInflightRead reader.
481+
func (d *DedupedMemfdCache) releaseMemfd() error {
482+
d.mu.Lock()
483+
defer d.mu.Unlock()
484+
if d.memfd == nil {
485+
return nil
486+
}
487+
err := d.memfd.Close()
488+
d.memfd = nil
489+
490+
return err
491+
}
492+
493+
// ServeMemfd copies [off, off+len(b)) from the still-mapped memfd using
494+
// identity (device) addressing, for a provisional local header that attributes
495+
// dirty pages to the memfd at their device offset. Returns BytesNotAvailableError
496+
// once the memfd has been released (drain finished), so the caller falls back to
497+
// the drained cache via the swapped-in deduped header.
498+
func (d *DedupedMemfdCache) ServeMemfd(b []byte, off int64) (int, error) {
499+
d.mu.RLock()
500+
defer d.mu.RUnlock()
501+
if d.memfd == nil {
502+
return 0, BytesNotAvailableError{}
503+
}
504+
src, err := d.memfd.Slice(off, int64(len(b)))
505+
if err != nil {
506+
return 0, err
507+
}
508+
copy(b, src)
509+
510+
return len(b), nil
511+
}
512+
478513
func (d *DedupedMemfdCache) Close() error {
479514
d.cancel()
480515
c, _ := d.done.Wait()
@@ -486,6 +521,51 @@ func (d *DedupedMemfdCache) Close() error {
486521
return nil
487522
}
488523

524+
// MemfdIdentitySource is the provisional local diff source: it serves dirty
525+
// pages from a DedupedMemfdCache's still-mapped memfd at identity (device)
526+
// offsets while dedup runs. It backs a distinct provisional build id; once the
527+
// deduped header is swapped in, reads route to the real build id instead and
528+
// this source is dropped. It is never uploaded.
529+
type MemfdIdentitySource struct {
530+
d *DedupedMemfdCache
531+
size int64
532+
}
533+
534+
func NewMemfdIdentitySource(d *DedupedMemfdCache, size int64) *MemfdIdentitySource {
535+
return &MemfdIdentitySource{d: d, size: size}
536+
}
537+
538+
func (s *MemfdIdentitySource) ReadAt(b []byte, off int64) (int, error) { return s.d.ServeMemfd(b, off) }
539+
540+
func (s *MemfdIdentitySource) Slice(off, length int64) ([]byte, error) {
541+
b := make([]byte, length)
542+
if _, err := s.d.ServeMemfd(b, off); err != nil {
543+
return nil, err
544+
}
545+
546+
return b, nil
547+
}
548+
549+
// IsCached reports the provisional source as resident while the memfd is mapped,
550+
// so dedup best-effort peeks and build.File.IsCached treat it as local.
551+
func (s *MemfdIdentitySource) IsCached(_ context.Context, off, length int64) bool {
552+
s.d.mu.RLock()
553+
defer s.d.mu.RUnlock()
554+
555+
return s.d.memfd != nil && off >= 0 && off+length <= s.size
556+
}
557+
558+
func (s *MemfdIdentitySource) Size() (int64, error) { return s.size, nil }
559+
func (s *MemfdIdentitySource) BlockSize() int64 { return header.PageSize }
560+
func (s *MemfdIdentitySource) Close() error { return nil }
561+
func (s *MemfdIdentitySource) FileSize(context.Context) (int64, error) { return s.size, nil }
562+
563+
// Path has no meaning for a memfd-backed source; it is local-only and never
564+
// uploaded, so the upload path (which needs a file path) must never reach it.
565+
func (s *MemfdIdentitySource) Path(context.Context) (string, error) {
566+
return "", errors.New("provisional memfd source has no path")
567+
}
568+
489569
func (d *DedupedMemfdCache) Path(ctx context.Context) (string, error) {
490570
c, err := d.Wait(ctx)
491571
if err != nil {

0 commit comments

Comments
 (0)