Skip to content

Commit d77b20f

Browse files
committed
NSFS | close stream when getting empty content dir
Signed-off-by: nadav mizrahi <[email protected]>
1 parent f9ea301 commit d77b20f

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed

src/sdk/namespace_fs.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ function filter_fs_xattr(xattr) {
301301

302302
/**
303303
* get_random_delay returns a random delay number between base + min and max
304-
* @param {number} base
305-
* @param {number} min
306-
* @param {number} max
304+
* @param {number} base
305+
* @param {number} min
306+
* @param {number} max
307307
* @returns {number}
308308
*/
309309
function get_random_delay(base, min, max) {
@@ -1022,7 +1022,11 @@ class NamespaceFS {
10221022
// NOTE: don't move this code after the open
10231023
// this can lead to ENOENT failures due to file not exists when content size is 0
10241024
// if entry is a directory object and its content size = 0 - return empty response
1025-
if (await this._is_empty_directory_content(file_path, fs_context, params)) return null;
1025+
if (await this._is_empty_directory_content(file_path, fs_context, params)) {
1026+
res.end();
1027+
await stream_utils.wait_finished(res, { signal: object_sdk.abort_controller.signal });
1028+
return null;
1029+
}
10261030

10271031
file = await nb_native().fs.open(
10281032
fs_context,
@@ -1312,14 +1316,14 @@ class NamespaceFS {
13121316

13131317
/**
13141318
* _check_copy_storage_class returns true if a copy is needed to be forced.
1315-
*
1319+
*
13161320
* This might be needed if we need to manage xattr separately on the source
13171321
* object and target object (eg. GLACIER objects).
1318-
*
1322+
*
13191323
* NOTE: The function will throw S3 error if source object storage class is
13201324
* "GLACIER" but it is not in restored state (AWS behaviour).
1321-
* @param {nb.NativeFSContext} fs_context
1322-
* @param {Record<any, any>} params
1325+
* @param {nb.NativeFSContext} fs_context
1326+
* @param {Record<any, any>} params
13231327
* @returns {Promise<boolean>}
13241328
*/
13251329
async _check_copy_storage_class(fs_context, params) {
@@ -1472,7 +1476,7 @@ class NamespaceFS {
14721476

14731477
// 1. get latest version_id
14741478
// 2. if versioning is suspended -
1475-
// 2.1 if version ID of the latest version is null -
1479+
// 2.1 if version ID of the latest version is null -
14761480
// 2.1.1. if it's POSIX backend - unlink the null version
14771481
// 2.1.2. if it's GPFS backend - nothing to do, the linkatif will override it
14781482
// 2.2 else (version ID of the latest version is unique or there is no latest version) -
@@ -1683,7 +1687,7 @@ class NamespaceFS {
16831687
const delimiter_idx = create_params_parsed.key.indexOf(params.delimiter, start_idx);
16841688
if (delimiter_idx > 0) {
16851689
common_prefixes_set.add(create_params_parsed.key.substring(0, delimiter_idx + 1));
1686-
// if key has common prefix it should not be returned as an upload object
1690+
// if key has common prefix it should not be returned as an upload object
16871691
return undefined;
16881692
}
16891693
}
@@ -1732,7 +1736,7 @@ class NamespaceFS {
17321736
return path.join(params.mpu_path, `part-${params.num}`);
17331737
}
17341738

1735-
// optimized version of upload_multipart -
1739+
// optimized version of upload_multipart -
17361740
// 1. if size is pre known -
17371741
// 1.1. calc offset
17381742
// 1.2. upload data to by_size file in offset position
@@ -2209,12 +2213,12 @@ class NamespaceFS {
22092213
/**
22102214
* restore_object simply sets the restore request xattr
22112215
* which should be picked by another mechanism.
2212-
*
2216+
*
22132217
* restore_object internally relies on 2 xattrs:
22142218
* - XATTR_RESTORE_REQUEST
22152219
* - XATTR_RESTORE_EXPIRY
2216-
* @param {*} params
2217-
* @param {nb.ObjectSDK} object_sdk
2220+
* @param {*} params
2221+
* @param {nb.ObjectSDK} object_sdk
22182222
* @returns {Promise<Object>}
22192223
*/
22202224
async restore_object(params, object_sdk) {
@@ -2352,7 +2356,7 @@ class NamespaceFS {
23522356
}
23532357

23542358
/**
2355-
*
2359+
*
23562360
* @param {*} fs_context - fs context object
23572361
* @param {string} file_path - path to file
23582362
* @param {*} set - the xattr object to be set
@@ -2706,7 +2710,7 @@ class NamespaceFS {
27062710
}
27072711
try {
27082712
// Returns the real path of the entry.
2709-
// The entry path may point to regular file or directory, but can have symbolic links
2713+
// The entry path may point to regular file or directory, but can have symbolic links
27102714
const full_path = await nb_native().fs.realpath(fs_context, entry_path);
27112715
if (!full_path.startsWith(this.bucket_path)) {
27122716
dbg.log0('check_bucket_boundaries: the path', entry_path, 'is not in the bucket', this.bucket_path, 'boundaries');
@@ -3380,9 +3384,9 @@ class NamespaceFS {
33803384
/**
33813385
* _check_version_moved recieves key and version_id and checks if the version still exists in one of the optional locations
33823386
* latest version location or .versions/ directory
3383-
* @param {import('./nb').NativeFSContext} fs_context
3384-
* @param {string} key
3385-
* @param {string} version_id
3387+
* @param {import('./nb').NativeFSContext} fs_context
3388+
* @param {string} key
3389+
* @param {string} version_id
33863390
*/
33873391
async _check_version_moved(fs_context, key, version_id) {
33883392
const latest_version_path = this._get_file_path({ key });

src/test/unit_tests/test_namespace_fs.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ mocha.describe('namespace_fs', function() {
547547
const dir_2 = '/a/b/';
548548
const upload_key_1 = dir_1 + 'upload_key_1/';
549549
const upload_key_2 = dir_2 + 'upload_key_2/';
550+
const upload_key_empty = 'empty_key/';
550551
const data = crypto.randomBytes(100);
551552

552553
mocha.before(async function() {
@@ -558,6 +559,22 @@ mocha.describe('namespace_fs', function() {
558559
console.log('upload_object with trailing / response', inspect(upload_res));
559560
});
560561

562+
mocha.it('get empty content dir', async function() {
563+
await ns_tmp.upload_object({
564+
bucket: upload_bkt,
565+
key: upload_key_empty,
566+
source_stream: buffer_utils.buffer_to_read_stream(crypto.randomBytes(0)),
567+
size: 0
568+
}, dummy_object_sdk);
569+
570+
const read_res = buffer_utils.write_stream();
571+
await ns_tmp.read_object_stream({
572+
bucket: upload_bkt,
573+
key: upload_key_empty,
574+
}, dummy_object_sdk, read_res);
575+
assert(read_res.writableEnded);
576+
});
577+
561578
mocha.it(`delete the path - stop when not empty and key with trailing /`, async function() {
562579
const upload_res = await ns_tmp.upload_object({
563580
bucket: upload_bkt,
@@ -574,11 +591,17 @@ mocha.describe('namespace_fs', function() {
574591
});
575592

576593
mocha.after(async function() {
577-
const delete_res = await ns_tmp.delete_object({
594+
let delete_res = await ns_tmp.delete_object({
578595
bucket: upload_bkt,
579596
key: upload_key_2,
580597
}, dummy_object_sdk);
581598
console.log('delete_object with trailing / (key 2) response', inspect(delete_res));
599+
600+
delete_res = await ns_tmp.delete_object({
601+
bucket: upload_bkt,
602+
key: upload_key_empty,
603+
}, dummy_object_sdk);
604+
console.log('delete_object with trailing / (empty dir) response', inspect(delete_res));
582605
});
583606
});
584607

0 commit comments

Comments
 (0)