Skip to content

Commit 5ad76e1

Browse files
authored
Reduced memory use on processing the new upload. (#8222)
1 parent 2ebde53 commit 5ad76e1

File tree

2 files changed

+27
-11
lines changed

2 files changed

+27
-11
lines changed

app/lib/package/backend.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -904,8 +904,7 @@ class PackageBackend {
904904
_logger.info('Examining tarball content ($guid).');
905905
final sw = Stopwatch()..start();
906906
final file = File(filename);
907-
final fileBytes = await file.readAsBytes();
908-
final sha256Hash = sha256.convert(fileBytes).bytes;
907+
final sha256Hash = (await file.openRead().transform(sha256).single).bytes;
909908
final archive = await summarizePackageArchive(
910909
filename,
911910
maxContentLength: maxAssetContentLength,
@@ -946,7 +945,7 @@ class PackageBackend {
946945
await tarballStorage.matchArchiveContentInCanonical(
947946
pubspec.name,
948947
versionString,
949-
fileBytes,
948+
file,
950949
);
951950
if (canonicalContentMatch == ContentMatchStatus.different) {
952951
throw PackageRejectedException.versionExists(

app/lib/package/tarball_storage.dart

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:typed_data';
5+
import 'dart:io';
66

77
import 'package:crypto/crypto.dart';
88
import 'package:gcloud/storage.dart';
@@ -102,26 +102,43 @@ class TarballStorage {
102102
Future<ContentMatchStatus> matchArchiveContentInCanonical(
103103
String package,
104104
String version,
105-
Uint8List bytes,
105+
File file,
106106
) async {
107107
final objectName = tarballObjectName(package, version);
108108
final info = await _canonicalBucket.tryInfo(objectName);
109109
if (info == null) {
110110
return ContentMatchStatus.missing;
111111
}
112-
if (info.length != bytes.length) {
112+
if (info.length != await file.length()) {
113113
return ContentMatchStatus.different;
114114
}
115-
final md5hash = md5.convert(bytes).bytes;
115+
final md5hash = (await file.openRead().transform(md5).single).bytes;
116116
if (!md5hash.byteToByteEquals(info.md5Hash)) {
117117
return ContentMatchStatus.different;
118118
}
119-
final objectBytes = await _canonicalBucket.readAsBytes(objectName);
120-
if (bytes.byteToByteEquals(objectBytes)) {
121-
return ContentMatchStatus.same;
122-
} else {
119+
// Limit memory use while doing the byte-to-byte comparison by streaming it chunk-wise.
120+
final raf = await file.open();
121+
var remainingLength = info.length;
122+
try {
123+
await for (final chunk in _canonicalBucket.read(objectName)) {
124+
if (chunk.isEmpty) continue;
125+
remainingLength -= chunk.length;
126+
if (remainingLength < 0) {
127+
return ContentMatchStatus.different;
128+
}
129+
// TODO: consider rewriting to fixed-length chunk comparison
130+
final fileChunk = await raf.read(chunk.length);
131+
if (!fileChunk.byteToByteEquals(chunk)) {
132+
return ContentMatchStatus.different;
133+
}
134+
}
135+
} finally {
136+
await raf.close();
137+
}
138+
if (remainingLength != 0) {
123139
return ContentMatchStatus.different;
124140
}
141+
return ContentMatchStatus.same;
125142
}
126143

127144
/// Copies the uploaded object from the temp bucket to the canonical bucket.

0 commit comments

Comments
 (0)