Skip to content

Commit 6900eff

Browse files
committed
NC | lifecycle | add noncurrent days rule
Signed-off-by: nadav mizrahi <[email protected]>
1 parent 826738a commit 6900eff

File tree

4 files changed

+128
-8
lines changed

4 files changed

+128
-8
lines changed

src/manage_nsfs/nc_lifecycle.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ class NCLifecycle {
580580

581581
/**
582582
* check if object is delete candidate based on newer noncurrent versions rule
583-
* @param {Object} object_info
583+
* @param {nb.ObjectInfo} object_info
584584
* @param {Object} newer_noncurrent_state
585585
* @param {Number} num_newer_versions
586586
* @returns
@@ -600,21 +600,22 @@ class NCLifecycle {
600600

601601
/**
602602
* check if object is delete candidate based on number of noncurrent days rule
603-
* @param {Object} object_info
603+
* @param {nb.ObjectInfo} object_info
604604
* @param {Number} num_non_current_days
605605
* @returns
606606
*/
607607
filter_noncurrent_days(object_info, num_non_current_days) {
608-
//TODO implement
609-
return true;
608+
if (object_info.is_latest) return false;
609+
const noncurrent_time = object_info.nc_noncurrent_time;
610+
return this._get_file_age_days(noncurrent_time) >= num_non_current_days;
610611
}
611612

612613
/**
613614
* get_candidates_by_noncurrent_version_expiration_rule processes the noncurrent version expiration rule
614615
* TODO:
615616
* POSIX - need to support both noncurrent_days and newer_noncurrent_versions
616617
* GPFS - implement noncurrent_days using GPFS ILM policy as an optimization
617-
* @param {*} lifecycle_rule
618+
* @param {Object} lifecycle_rule
618619
* @param {Object} bucket_json
619620
* @returns {Promise<Object[]>}
620621
*/

src/sdk/namespace_fs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2634,6 +2634,8 @@ class NamespaceFS {
26342634
const storage_class = Glacier.storage_class_from_xattr(stat.xattr);
26352635
const size = Number(stat.xattr?.[XATTR_DIR_CONTENT] || stat.size);
26362636
const tag_count = stat.xattr ? this._number_of_tags_fs_xttr(stat.xattr) : 0;
2637+
const nc_noncurrent_time = (stat.xattr?.[XATTR_NON_CURRENT_TIMESTASMP] && Number(stat.xattr[XATTR_NON_CURRENT_TIMESTASMP])) ||
2638+
stat.ctime.getTime();
26372639

26382640
return {
26392641
obj_id: etag,
@@ -2652,6 +2654,7 @@ class NamespaceFS {
26522654
xattr: to_xattr(stat.xattr),
26532655
tag_count,
26542656
tagging: get_tags_from_xattr(stat.xattr),
2657+
nc_noncurrent_time,
26552658

26562659
// temp:
26572660
lock_settings: undefined,

src/sdk/nb.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ interface ObjectInfo {
442442
restore_status?: RestoreStatus;
443443
checksum?: Checksum;
444444
object_parts?: GetObjectAttributesParts;
445+
nc_noncurrent_time ?: number;
445446
}
446447

447448

src/test/unit_tests/jest_tests/test_nc_lifecycle_posix_integration.test.js

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const buffer_utils = require('../../../util/buffer_utils');
2121
const crypto = require('crypto');
2222
const NsfsObjectSDK = require('../../../sdk/nsfs_object_sdk');
2323
const nb_native = require('../../../util/nb_native');
24+
const native_fs_utils = require('../../../util/native_fs_utils');
2425

2526
const LIFECYCLE_RULE_STATUS_ENUM = Object.freeze({
2627
ENABLED: 'Enabled',
@@ -578,6 +579,93 @@ describe('noobaa nc - lifecycle versioning ENABLE', () => {
578579
});
579580
expect(has_delete_marker).toBe(true);
580581
});
582+
583+
it('nc lifecycle - noncurrent expiration rule - expire older versions by number of days with filter - regular key', async () => {
584+
const lifecycle_rule = [{
585+
"id": "expire noncurrent versions after 3 days with size ",
586+
"status": LIFECYCLE_RULE_STATUS_ENUM.ENABLED,
587+
"filter": {
588+
"prefix": prefix,
589+
"object_size_greater_than": 80,
590+
},
591+
"noncurrent_version_expiration": {
592+
"noncurrent_days": 3
593+
}
594+
}];
595+
await object_sdk.set_bucket_lifecycle_configuration_rules({ name: test_bucket, rules: lifecycle_rule });
596+
597+
let res = await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
598+
await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
599+
await update_version_xattr(test_bucket, test_key1_regular, res.version_id);
600+
601+
const expected_res = await create_object(object_sdk, test_bucket, test_prefix_key_regular, 100, false);
602+
res = await create_object(object_sdk, test_bucket, test_prefix_key_regular, 60, false);
603+
await create_object(object_sdk, test_bucket, test_prefix_key_regular, 100, false);
604+
await create_object(object_sdk, test_bucket, test_prefix_key_regular, 100, false);
605+
await update_version_xattr(test_bucket, test_prefix_key_regular, expected_res.version_id);
606+
await update_version_xattr(test_bucket, test_prefix_key_regular, res.version_id);
607+
608+
await exec_manage_cli(TYPES.LIFECYCLE, '', { disable_service_validation: 'true', disable_runtime_validation: 'true', config_root }, undefined, undefined);
609+
const object_list = await object_sdk.list_object_versions({ bucket: test_bucket });
610+
expect(object_list.objects.length).toBe(5);
611+
object_list.objects.forEach(element => {
612+
expect(element.version_id).not.toBe(expected_res.version_id);
613+
});
614+
});
615+
616+
it('nc lifecycle - noncurrent expiration rule - both noncurrent days and older versions', async () => {
617+
const lifecycle_rule = [{
618+
"id": "expire noncurrent versions after 3 days with size ",
619+
"status": LIFECYCLE_RULE_STATUS_ENUM.ENABLED,
620+
"filter": {
621+
"prefix": '',
622+
},
623+
"noncurrent_version_expiration": {
624+
"noncurrent_days": 3,
625+
"newer_noncurrent_versions": 1
626+
}
627+
}];
628+
await object_sdk.set_bucket_lifecycle_configuration_rules({ name: test_bucket, rules: lifecycle_rule });
629+
630+
const expected_res = await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
631+
const res = await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
632+
await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
633+
// older than 3 days but no more than one noncurrent version - don't delete
634+
await update_version_xattr(test_bucket, test_key1_regular, expected_res.version_id);
635+
// both older than 3 days and more than one noncurrent version - delete
636+
await update_version_xattr(test_bucket, test_key1_regular, res.version_id);
637+
638+
await exec_manage_cli(TYPES.LIFECYCLE, '', { disable_service_validation: 'true', disable_runtime_validation: 'true', config_root }, undefined, undefined);
639+
const object_list = await object_sdk.list_object_versions({ bucket: test_bucket });
640+
expect(object_list.objects.length).toBe(2);
641+
object_list.objects.forEach(element => {
642+
expect(element.version_id).not.toBe(expected_res.version_id);
643+
});
644+
});
645+
646+
it('nc lifecycle - noncurrent expiration rule - older versions valid but noncurrent_days not valid', async () => {
647+
const lifecycle_rule = [{
648+
"id": "expire noncurrent versions after 3 days with size ",
649+
"status": LIFECYCLE_RULE_STATUS_ENUM.ENABLED,
650+
"filter": {
651+
"prefix": '',
652+
},
653+
"noncurrent_version_expiration": {
654+
"noncurrent_days": 3,
655+
"newer_noncurrent_versions": 1
656+
}
657+
}];
658+
await object_sdk.set_bucket_lifecycle_configuration_rules({ name: test_bucket, rules: lifecycle_rule });
659+
660+
await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
661+
await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
662+
// more than one noncurrent version but not older than 3 days - don't delete
663+
await create_object(object_sdk, test_bucket, test_key1_regular, 100, false);
664+
665+
await exec_manage_cli(TYPES.LIFECYCLE, '', { disable_service_validation: 'true', disable_runtime_validation: 'true', config_root }, undefined, undefined);
666+
const object_list = await object_sdk.list_object_versions({ bucket: test_bucket });
667+
expect(object_list.objects.length).toBe(3);
668+
});
581669
});
582670

583671
describe('noobaa nc - lifecycle versioning ENABLE - expiration rule - delete marker', () => {
@@ -1682,16 +1770,16 @@ describe('noobaa nc - lifecycle notifications', () => {
16821770
/**
16831771
* create_object creates an object with random data in the bucket
16841772
* Note: is_old - if true, would update the mtime of the file.
1685-
* @param {object} sdk
1773+
* @param {object} object_sdk
16861774
* @param {string} bucket
16871775
* @param {string} key
16881776
* @param {number} size
16891777
* @param {boolean} [is_old]
16901778
* @param {{ key: string; value: string; }[]} [tagging]
16911779
*/
1692-
async function create_object(sdk, bucket, key, size, is_old, tagging) {
1780+
async function create_object(object_sdk, bucket, key, size, is_old, tagging) {
16931781
const data = crypto.randomBytes(size);
1694-
const res = await sdk.upload_object({
1782+
const res = await object_sdk.upload_object({
16951783
bucket,
16961784
key,
16971785
source_stream: buffer_utils.buffer_to_read_stream(data),
@@ -1720,6 +1808,33 @@ async function update_file_mtime(target_path) {
17201808
await os_utils.exec(update_file_mtime_cmp, { return_stdout: true });
17211809
}
17221810

1811+
/**
1812+
* updates the number of noncurrent days xattr of target path to be 5 days older. use only on noncurrent objects.
1813+
* is use this function on latest object the xattr will be changed when the object turns noncurrent
1814+
* how to use this function:
1815+
* 1. create a new object but don't change its mtime (changing mtime will cause versioning functions to fail)
1816+
* 2. create a new object with the same key to make the object noncurrent
1817+
* 3. call this function to change the xattr of the noncurrent object
1818+
* @param {String} bucket
1819+
* @param {String} key
1820+
* @param {String} version_id
1821+
* @returns {Promise<Void>}
1822+
*/
1823+
async function update_version_xattr(bucket, key, version_id) {
1824+
const older_time = new Date();
1825+
older_time.setDate(yesterday.getDate() - 5); // 5 days ago
1826+
1827+
const target_path = path.join(root_path, bucket, path.dirname(key), '.versions', `${path.basename(key)}_${version_id}`);
1828+
const file = await nb_native().fs.open(config_fs.fs_context, target_path, config.NSFS_OPEN_READ_MODE,
1829+
native_fs_utils.get_umasked_mode(config.BASE_MODE_FILE));
1830+
const stat = await file.stat(config_fs.fs_context);
1831+
const xattr = Object.assign(stat.xattr, {
1832+
'user.noobaa.non_current_timestamp': older_time.getTime(),
1833+
});
1834+
await file.replacexattr(config_fs.fs_context, xattr, undefined);
1835+
await file.close(config_fs.fs_context);
1836+
}
1837+
17231838
/**
17241839
* date_to_run_time_format coverts a date to run time format HH:MM
17251840
* @param {Date} date

0 commit comments

Comments
 (0)