authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-05 17:36:14-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-05 17:36:14-08:00
logd8171e8a2ee56e76bcd91f187d5ca5664b87bc83
tree15d475879933c9ccce5493222cd0b0b2ca261dba
parent1f65e7cccc3124ce2fa9a5e8107c32367a39ec29

fetch: check global cache for compressed tarball

before remote URL

1 files changed, 51 insertions(+), 21 deletions(-)

src/Package/Fetch.zig+51-21
...@@ -40,6 +40,7 @@ const native_os = builtin.os.tag;...@@ -40,6 +40,7 @@ const native_os = builtin.os.tag;
40const std = @import("std");40const std = @import("std");
41const Io = std.Io;41const Io = std.Io;
42const fs = std.fs;42const fs = std.fs;
43const log = std.log.scoped(.fetch);
43const assert = std.debug.assert;44const assert = std.debug.assert;
44const ascii = std.ascii;45const ascii = std.ascii;
45const Allocator = std.mem.Allocator;46const Allocator = std.mem.Allocator;
...@@ -324,7 +325,7 @@ pub const JobQueue = struct {...@@ -324,7 +325,7 @@ pub const JobQueue = struct {
324 error.Canceled => |e| return e,325 error.Canceled => |e| return e,
325 error.ReadFailed => comptime unreachable,326 error.ReadFailed => comptime unreachable,
326 error.WriteFailed => comptime unreachable,327 error.WriteFailed => comptime unreachable,
327 else => |e| std.log.warn("failed caching recompressed tarball to {f}: {t}", .{ dest_path, e }),328 else => |e| log.warn("failed caching recompressed tarball to {f}: {t}", .{ dest_path, e }),
328 };329 };
329 }330 }
330331
...@@ -508,14 +509,14 @@ pub fn run(f: *Fetch) RunError!void {...@@ -508,14 +509,14 @@ pub fn run(f: *Fetch) RunError!void {
508 .path_or_url => |path_or_url| {509 .path_or_url => |path_or_url| {
509 if (Io.Dir.cwd().openDir(io, path_or_url, .{ .iterate = true })) |dir| {510 if (Io.Dir.cwd().openDir(io, path_or_url, .{ .iterate = true })) |dir| {
510 var resource: Resource = .{ .dir = dir };511 var resource: Resource = .{ .dir = dir };
511 return f.runResource(path_or_url, &resource, null);512 return f.runResource(path_or_url, &resource, null, false);
512 } else |dir_err| {513 } else |dir_err| {
513 var server_header_buffer: [init_resource_buffer_size]u8 = undefined;514 var server_header_buffer: [init_resource_buffer_size]u8 = undefined;
514515
515 const file_err = if (dir_err == error.NotDir) e: {516 const file_err = if (dir_err == error.NotDir) e: {
516 if (Io.Dir.cwd().openFile(io, path_or_url, .{})) |file| {517 if (Io.Dir.cwd().openFile(io, path_or_url, .{})) |file| {
517 var resource: Resource = .{ .file = file.reader(io, &server_header_buffer) };518 var resource: Resource = .{ .file = file.reader(io, &server_header_buffer) };
518 return f.runResource(path_or_url, &resource, null);519 return f.runResource(path_or_url, &resource, null, false);
519 } else |err| break :e err;520 } else |err| break :e err;
520 } else dir_err;521 } else dir_err;
521522
...@@ -527,11 +528,13 @@ pub fn run(f: *Fetch) RunError!void {...@@ -527,11 +528,13 @@ pub fn run(f: *Fetch) RunError!void {
527 };528 };
528 var resource: Resource = undefined;529 var resource: Resource = undefined;
529 try f.initResource(uri, &resource, &server_header_buffer);530 try f.initResource(uri, &resource, &server_header_buffer);
530 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null);531 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null, false);
531 }532 }
532 },533 },
533 };534 };
534535
536 var resource_buffer: [init_resource_buffer_size]u8 = undefined;
537
535 if (remote.hash) |expected_hash| {538 if (remote.hash) |expected_hash| {
536 const package_root = try job_queue.root_pkg_path.join(arena, expected_hash.toSlice());539 const package_root = try job_queue.root_pkg_path.join(arena, expected_hash.toSlice());
537 if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| {540 if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| {
...@@ -543,19 +546,13 @@ pub fn run(f: *Fetch) RunError!void {...@@ -543,19 +546,13 @@ pub fn run(f: *Fetch) RunError!void {
543 return queueJobsForDeps(f);546 return queueJobsForDeps(f);
544 } else |err| switch (err) {547 } else |err| switch (err) {
545 error.FileNotFound => {548 error.FileNotFound => {
546 switch (f.lazy_status) {549 log.debug("FileNotFound: {f}", .{package_root});
547 .eager => {},
548 .available => if (!job_queue.unlazy_set.contains(expected_hash)) {
549 f.lazy_status = .unavailable;
550 return;
551 },
552 .unavailable => unreachable,
553 }
554 if (job_queue.read_only) return f.fail(550 if (job_queue.read_only) return f.fail(
555 f.name_tok,551 f.name_tok,
556 try eb.printString("package not found at '{f}'", .{package_root}),552 try eb.printString("package not found at '{f}'", .{package_root}),
557 );553 );
558 },554 },
555 error.Canceled => |e| return e,
559 else => |e| {556 else => |e| {
560 try eb.addRootErrorMessage(.{557 try eb.addRootErrorMessage(.{
561 .msg = try eb.printString("unable to open package cache directory {f}: {t}", .{558 .msg = try eb.printString("unable to open package cache directory {f}: {t}", .{
...@@ -565,6 +562,38 @@ pub fn run(f: *Fetch) RunError!void {...@@ -565,6 +562,38 @@ pub fn run(f: *Fetch) RunError!void {
565 return error.FetchFailed;562 return error.FetchFailed;
566 },563 },
567 }564 }
565
566 // Check global cache before remote fetch.
567 const cached_tarball_sub_path = try std.fmt.allocPrint(arena, "p/{s}.tar.gz", .{expected_hash.toSlice()});
568 const cached_tarball_path: Cache.Path = .{
569 .root_dir = job_queue.global_cache,
570 .sub_path = cached_tarball_sub_path,
571 };
572 if (cached_tarball_path.root_dir.handle.openFile(io, cached_tarball_path.sub_path, .{})) |file| {
573 log.debug("found global cached tarball {f}", .{cached_tarball_path});
574 var resource: Resource = .{ .file = file.reader(io, &resource_buffer) };
575 return f.runResource(cached_tarball_sub_path, &resource, remote.hash, true);
576 } else |err| switch (err) {
577 error.FileNotFound => log.debug("FileNotFound: {f}", .{cached_tarball_path}),
578 error.Canceled => |e| return e,
579 else => |e| {
580 try eb.addRootErrorMessage(.{
581 .msg = try eb.printString("unable to open globally cached package {f}: {t}", .{
582 cached_tarball_path, e,
583 }),
584 });
585 return error.FetchFailed;
586 },
587 }
588
589 switch (f.lazy_status) {
590 .eager => {},
591 .available => if (!job_queue.unlazy_set.contains(expected_hash)) {
592 f.lazy_status = .unavailable;
593 return;
594 },
595 .unavailable => unreachable,
596 }
568 } else if (job_queue.read_only) {597 } else if (job_queue.read_only) {
569 try eb.addRootErrorMessage(.{598 try eb.addRootErrorMessage(.{
570 .msg = try eb.addString("dependency is missing hash field"),599 .msg = try eb.addString("dependency is missing hash field"),
...@@ -574,15 +603,13 @@ pub fn run(f: *Fetch) RunError!void {...@@ -574,15 +603,13 @@ pub fn run(f: *Fetch) RunError!void {
574 }603 }
575604
576 // Fetch and unpack the remote into a temporary directory.605 // Fetch and unpack the remote into a temporary directory.
577
578 const uri = std.Uri.parse(remote.url) catch |err| return f.fail(606 const uri = std.Uri.parse(remote.url) catch |err| return f.fail(
579 f.location_tok,607 f.location_tok,
580 try eb.printString("invalid URI: {t}", .{err}),608 try eb.printString("invalid URI: {t}", .{err}),
581 );609 );
582 var buffer: [init_resource_buffer_size]u8 = undefined;
583 var resource: Resource = undefined;610 var resource: Resource = undefined;
584 try f.initResource(uri, &resource, &buffer);611 try f.initResource(uri, &resource, &resource_buffer);
585 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash);612 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash, false);
586}613}
587614
588pub fn deinit(f: *Fetch) void {615pub fn deinit(f: *Fetch) void {
...@@ -596,6 +623,7 @@ fn runResource(...@@ -596,6 +623,7 @@ fn runResource(
596 uri_path: []const u8,623 uri_path: []const u8,
597 resource: *Resource,624 resource: *Resource,
598 remote_hash: ?Package.Hash,625 remote_hash: ?Package.Hash,
626 disable_recompress: bool,
599) RunError!void {627) RunError!void {
600 const job_queue = f.job_queue;628 const job_queue = f.job_queue;
601 assert(!job_queue.read_only);629 assert(!job_queue.read_only);
...@@ -681,15 +709,17 @@ fn runResource(...@@ -681,15 +709,17 @@ fn runResource(
681 return error.FetchFailed;709 return error.FetchFailed;
682 };710 };
683711
684 // Spin off a task to recompress the tarball, with filtered files deleted, into712 if (!disable_recompress) {
685 // the global cache.713 // Spin off a task to recompress the tarball, with filtered files deleted, into
686 job_queue.group.async(io, JobQueue.recompress, .{ job_queue, computed_package_hash });714 // the global cache.
715 job_queue.group.async(io, JobQueue.recompress, .{ job_queue, computed_package_hash });
716 }
687717
688 // Remove temporary directory root if not already renamed to global cache.718 // Remove temporary directory root if not already renamed to global cache.
689 if (!package_sub_path.eql(tmp_directory_path)) {719 if (!package_sub_path.eql(tmp_directory_path)) {
690 tmp_directory_path.root_dir.handle.deleteDir(io, tmp_directory_path.sub_path) catch |err| switch (err) {720 tmp_directory_path.root_dir.handle.deleteDir(io, tmp_directory_path.sub_path) catch |err| switch (err) {
691 error.Canceled => |e| return e,721 error.Canceled => |e| return e,
692 else => |e| std.log.warn("failed to delete temporary directory {f}: {t}", .{ tmp_directory_path, e }),722 else => |e| log.warn("failed to delete temporary directory {f}: {t}", .{ tmp_directory_path, e }),
693 };723 };
694 }724 }
695725
...@@ -1588,7 +1618,7 @@ pub fn renameTmpIntoCache(io: Io, tmp_path: Cache.Path, dest_path: Cache.Path) !...@@ -1588,7 +1618,7 @@ pub fn renameTmpIntoCache(io: Io, tmp_path: Cache.Path, dest_path: Cache.Path) !
1588 error.Canceled => |e| return e,1618 error.Canceled => |e| return e,
1589 // Garbage files leftover in zig-cache/tmp/ is, as they say1619 // Garbage files leftover in zig-cache/tmp/ is, as they say
1590 // on Star Trek, "operating within normal parameters".1620 // on Star Trek, "operating within normal parameters".
1591 else => |e| std.log.warn("failed to delete temporary directory {f}: {t}", .{ tmp_path, e }),1621 else => |e| log.warn("failed to delete temporary directory {f}: {t}", .{ tmp_path, e }),
1592 };1622 };
1593 },1623 },
1594 else => |e| return e,1624 else => |e| return e,