Skip to content

Commit efefe8e

Browse files
Merge pull request #8894 from tangledbytes/utkarsh/backport/5.18/DFBUGS-1871
[Backport 5.18] PR 8028,8878,8810,8880
2 parents 65045b6 + 4c9b219 commit efefe8e

File tree

12 files changed

+601
-170
lines changed

12 files changed

+601
-170
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ config-local.js
2323
*.sublime*
2424
.DS_Store
2525
heapdump-*
26+
.cache
27+
.clangd
2628

2729
## PRIVATE
2830
*.pem

config.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,44 @@ config.NSFS_GLACIER_EXPIRY_TZ = 'LOCAL';
869869
// the request will be used
870870
config.NSFS_GLACIER_EXPIRY_TIME_OF_DAY = '';
871871

872+
// If set to to true, NooBaa will attempt to read DMAPI
873+
// xattrs
874+
config.NSFS_GLACIER_DMAPI_ENABLE = false;
875+
876+
// NSFS_GLACIER_DMAPI_IMPLICIT_RESTORE_STATUS if enabled then
877+
// NooBaa will derive restore status of the files based on DMAPI
878+
// xattr IF there are no explicit restore status attributes on
879+
// the file.
880+
config.NSFS_GLACIER_DMAPI_IMPLICIT_RESTORE_STATUS = false;
881+
882+
// If set to true then NooBaa will consider DMAPI extended attributes
883+
// in conjuction with NooBaa's `user.storage_class` extended attribute
884+
// to determine state of an object.
885+
//
886+
// NOTE:NSFS_GLACIER_DMAPI_ENABLE should be enabled to use this.
887+
config.NSFS_GLACIER_DMAPI_IMPLICIT_SC = false;
888+
889+
// NSFS_GLACIER_DMAPI_ALLOW_NOOBAA_TAKEOVER allows NooBaa to take over lifecycle
890+
// management of an object which was originally NOT managed by NooBaa.
891+
//
892+
// NOTE:NSFS_GLACIER_DMAPI_ENABLE and NSFS_GLACIER_USE_DMAPI should be enabled to use this.
893+
config.NSFS_GLACIER_DMAPI_ALLOW_NOOBAA_TAKEOVER = false;
894+
895+
// NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER_ENABLE if true will add additional HTTP headers
896+
// `config.NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER` based on `dmapi.IBMTPS` EA.
897+
//
898+
// NOTE:NSFS_GLACIER_DMAPI_ENABLE should be enabled to use this.
899+
config.NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER_ENABLE = false;
900+
config.NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER = 'x-tape-meta-copy';
901+
902+
// NSFS_GLACIER_DMAPI_PMIG_DAYS controls the "virtual"/fake expiry
903+
// days that will be shown if we detect a glacier object whose life-
904+
// cycle NSFS doesn't controls
905+
//
906+
// This is initialized to be the same as S3_RESTORE_REQUEST_MAX_DAYS
907+
// but can be overridden to any numberical value
908+
config.NSFS_GLACIER_DMAPI_PMIG_DAYS = config.S3_RESTORE_REQUEST_MAX_DAYS;
909+
872910
config.NSFS_STATFS_CACHE_SIZE = 10000;
873911
config.NSFS_STATFS_CACHE_EXPIRY_MS = 1 * 1000;
874912

@@ -897,6 +935,16 @@ config.NSFS_LOW_FREE_SPACE_MB_UNLEASH = 10 * 1024;
897935
// operations safely.
898936
config.NSFS_LOW_FREE_SPACE_PERCENT_UNLEASH = 0.10;
899937

938+
// NSFS_GLACIER_GET_FORCE_EXPIRE if set to true then any restored item in the GLACIER
939+
// storage class will expire as soon as first GET request is received for it or
940+
// if the previous restore time has exceed, whichever is the earlier.
941+
config.NSFS_GLACIER_FORCE_EXPIRE_ON_GET = false;
942+
943+
// NSFS_GLACIER_MIGRATE_LOG_THRESHOLD controls that how big the migration log file should be
944+
// Once this size is exceeded, migrate calls are supposed to kick in regardless of configured
945+
// interval
946+
config.NSFS_GLACIER_MIGRATE_LOG_THRESHOLD = 50 * 1024;
947+
900948
// anonymous account name
901949
config.ANONYMOUS_ACCOUNT_NAME = 'anonymous';
902950

src/endpoint/s3/s3_utils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'use strict';
33

44
const _ = require('lodash');
5+
const querystring = require('querystring');
56

67
const dbg = require('../../util/debug_module')(__filename);
78
const S3Error = require('./s3_errors').S3Error;
@@ -325,6 +326,13 @@ function set_response_object_md(res, object_md) {
325326

326327
res.setHeader('x-amz-restore', restore);
327328
}
329+
if (config.NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER_ENABLE) {
330+
object_md.restore_status?.tape_info?.forEach?.((meta, idx) => {
331+
// @ts-ignore - For some TS check doesn't like "meta" being passed to querystring
332+
const header = querystring.stringify(meta);
333+
res.setHeader(`${config.NSFS_GLACIER_DMAPI_TPS_HTTP_HEADER}-${idx}`, header);
334+
});
335+
}
328336
}
329337

330338
/** set_response_headers_get_object_attributes is based on set_response_object_md

src/manage_nsfs/manage_nsfs_glacier.js

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ const path = require('path');
55
const { PersistentLogger } = require('../util/persistent_logger');
66
const config = require('../../config');
77
const nb_native = require('../util/nb_native');
8-
const { GlacierBackend } = require('../sdk/nsfs_glacier_backend/backend');
9-
const { getGlacierBackend } = require('../sdk/nsfs_glacier_backend/helper');
8+
const { Glacier } = require('../sdk/glacier');
109
const native_fs_utils = require('../util/native_fs_utils');
1110

1211
const CLUSTER_LOCK = 'cluster.lock';
@@ -16,14 +15,15 @@ async function process_migrations() {
1615
const fs_context = native_fs_utils.get_process_fs_context();
1716

1817
await lock_and_run(fs_context, CLUSTER_LOCK, async () => {
19-
const backend = getGlacierBackend();
18+
const backend = Glacier.getBackend();
2019

2120
if (
2221
await backend.low_free_space() ||
23-
await time_exceeded(fs_context, config.NSFS_GLACIER_MIGRATE_INTERVAL, GlacierBackend.MIGRATE_TIMESTAMP_FILE)
22+
await time_exceeded(fs_context, config.NSFS_GLACIER_MIGRATE_INTERVAL, Glacier.MIGRATE_TIMESTAMP_FILE) ||
23+
await migrate_log_exceeds_threshold()
2424
) {
2525
await run_glacier_migrations(fs_context, backend);
26-
await record_current_time(fs_context, GlacierBackend.MIGRATE_TIMESTAMP_FILE);
26+
await record_current_time(fs_context, Glacier.MIGRATE_TIMESTAMP_FILE);
2727
}
2828
});
2929
}
@@ -32,56 +32,56 @@ async function process_migrations() {
3232
* run_tape_migrations reads the migration WALs and attempts to migrate the
3333
* files mentioned in the WAL.
3434
* @param {nb.NativeFSContext} fs_context
35-
* @param {import('../sdk/nsfs_glacier_backend/backend').GlacierBackend} backend
35+
* @param {import('../sdk/glacier').Glacier} backend
3636
*/
3737
async function run_glacier_migrations(fs_context, backend) {
38-
await run_glacier_operation(fs_context, GlacierBackend.MIGRATE_WAL_NAME, backend.migrate.bind(backend));
38+
await run_glacier_operation(fs_context, Glacier.MIGRATE_WAL_NAME, backend.migrate.bind(backend));
3939
}
4040

4141
async function process_restores() {
4242
const fs_context = native_fs_utils.get_process_fs_context();
4343

4444
await lock_and_run(fs_context, CLUSTER_LOCK, async () => {
45-
const backend = getGlacierBackend();
45+
const backend = Glacier.getBackend();
4646

4747
if (
4848
await backend.low_free_space() ||
49-
!(await time_exceeded(fs_context, config.NSFS_GLACIER_RESTORE_INTERVAL, GlacierBackend.RESTORE_TIMESTAMP_FILE))
49+
!(await time_exceeded(fs_context, config.NSFS_GLACIER_RESTORE_INTERVAL, Glacier.RESTORE_TIMESTAMP_FILE))
5050
) return;
5151

5252

5353
await run_glacier_restore(fs_context, backend);
54-
await record_current_time(fs_context, GlacierBackend.RESTORE_TIMESTAMP_FILE);
54+
await record_current_time(fs_context, Glacier.RESTORE_TIMESTAMP_FILE);
5555
});
5656
}
5757

5858
/**
5959
* run_tape_restore reads the restore WALs and attempts to restore the
6060
* files mentioned in the WAL.
61-
* @param {nb.NativeFSContext} fs_context
62-
* @param {import('../sdk/nsfs_glacier_backend/backend').GlacierBackend} backend
61+
* @param {nb.NativeFSContext} fs_context
62+
* @param {import('../sdk/glacier').Glacier} backend
6363
*/
6464
async function run_glacier_restore(fs_context, backend) {
65-
await run_glacier_operation(fs_context, GlacierBackend.RESTORE_WAL_NAME, backend.restore.bind(backend));
65+
await run_glacier_operation(fs_context, Glacier.RESTORE_WAL_NAME, backend.restore.bind(backend));
6666
}
6767

6868
async function process_expiry() {
6969
const fs_context = native_fs_utils.get_process_fs_context();
7070

7171
await lock_and_run(fs_context, SCAN_LOCK, async () => {
72-
const backend = getGlacierBackend();
72+
const backend = Glacier.getBackend();
7373
if (
7474
await backend.low_free_space() ||
7575
await is_desired_time(
7676
fs_context,
7777
new Date(),
7878
config.NSFS_GLACIER_EXPIRY_RUN_TIME,
7979
config.NSFS_GLACIER_EXPIRY_RUN_DELAY_LIMIT_MINS,
80-
GlacierBackend.EXPIRY_TIMESTAMP_FILE,
80+
Glacier.EXPIRY_TIMESTAMP_FILE,
8181
)
8282
) {
8383
await backend.expiry(fs_context);
84-
await record_current_time(fs_context, GlacierBackend.EXPIRY_TIMESTAMP_FILE);
84+
await record_current_time(fs_context, Glacier.EXPIRY_TIMESTAMP_FILE);
8585
}
8686
});
8787
}
@@ -168,6 +168,27 @@ async function record_current_time(fs_context, timestamp_file) {
168168
);
169169
}
170170

171+
/**
172+
* migrate_log_exceeds_threshold returns true if the underlying backend
173+
* decides that the migrate log size has exceeded the given size threshold.
174+
* @param {number} [threshold]
175+
* @returns {Promise<boolean>}
176+
*/
177+
async function migrate_log_exceeds_threshold(threshold = config.NSFS_GLACIER_MIGRATE_LOG_THRESHOLD) {
178+
const log = new PersistentLogger(config.NSFS_GLACIER_LOGS_DIR, Glacier.MIGRATE_WAL_NAME, { locking: null });
179+
let log_size = Number.MAX_SAFE_INTEGER;
180+
try {
181+
const fh = await log._open();
182+
183+
const { size } = await fh.stat(log.fs_context);
184+
log_size = size;
185+
} catch (error) {
186+
console.error("failed to get size of", Glacier.MIGRATE_WAL_NAME, error);
187+
}
188+
189+
return log_size > threshold;
190+
}
191+
171192
/**
172193
* run_glacier_operations takes a log_namespace and a callback and executes the
173194
* callback on each log file in that namespace. It will also generate a failure
@@ -178,6 +199,8 @@ async function record_current_time(fs_context, timestamp_file) {
178199
*/
179200
async function run_glacier_operation(fs_context, log_namespace, cb) {
180201
const log = new PersistentLogger(config.NSFS_GLACIER_LOGS_DIR, log_namespace, { locking: 'EXCLUSIVE' });
202+
203+
fs_context = prepare_galcier_fs_context(fs_context);
181204
try {
182205
await log.process(async (entry, failure_recorder) => cb(fs_context, entry, failure_recorder));
183206
} catch (error) {
@@ -230,6 +253,28 @@ async function lock_and_run(fs_context, lockfilename, cb) {
230253
}
231254
}
232255

256+
/**
257+
* prepare_galcier_fs_context returns a shallow copy of given
258+
* fs_context with backend set to 'GPFS'.
259+
*
260+
* NOTE: The function will throw error if it detects that libgfs
261+
* isn't loaded.
262+
*
263+
* @param {nb.NativeFSContext} fs_context
264+
* @returns {nb.NativeFSContext}
265+
*/
266+
function prepare_galcier_fs_context(fs_context) {
267+
if (config.NSFS_GLACIER_DMAPI_ENABLE) {
268+
if (!nb_native().fs.gpfs) {
269+
throw new Error('cannot use DMAPI xattrs: libgpfs not loaded');
270+
}
271+
272+
return { ...fs_context, backend: 'GPFS', use_dmapi: true };
273+
}
274+
275+
return { ...fs_context };
276+
}
277+
233278
exports.process_migrations = process_migrations;
234279
exports.process_restores = process_restores;
235280
exports.process_expiry = process_expiry;

src/native/fs/fs_napi.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
#define GPFS_XATTR_PREFIX "gpfs"
4747
#define GPFS_DOT_ENCRYPTION_EA "Encryption"
4848
#define GPFS_ENCRYPTION_XATTR_NAME GPFS_XATTR_PREFIX "." GPFS_DOT_ENCRYPTION_EA
49+
#define GPFS_DMAPI_XATTR_PREFIX "dmapi"
50+
#define GPFS_DMAPI_DOT_IBMOBJ_EA "IBMObj"
51+
#define GPFS_DMAPI_DOT_IBMPMIG_EA "IBMPMig"
52+
#define GPFS_DMAPI_DOT_IBMTPS_EA "IBMTPS"
53+
#define GPFS_DMAPI_XATTR_TAPE_INDICATOR GPFS_DMAPI_XATTR_PREFIX "." GPFS_DMAPI_DOT_IBMOBJ_EA
54+
#define GPFS_DMAPI_XATTR_TAPE_PREMIG GPFS_DMAPI_XATTR_PREFIX "." GPFS_DMAPI_DOT_IBMPMIG_EA
55+
#define GPFS_DMAPI_XATTR_TAPE_TPS GPFS_DMAPI_XATTR_PREFIX "." GPFS_DMAPI_DOT_IBMTPS_EA
4956

5057
// This macro should be used after openning a file
5158
// it will autoclose the file using AutoCloser and will throw an error in case of failures
@@ -244,6 +251,11 @@ parse_open_flags(std::string flags)
244251
}
245252

246253
const static std::vector<std::string> GPFS_XATTRS{ GPFS_ENCRYPTION_XATTR_NAME };
254+
const static std::vector<std::string> GPFS_DMAPI_XATTRS{
255+
GPFS_DMAPI_XATTR_TAPE_INDICATOR,
256+
GPFS_DMAPI_XATTR_TAPE_PREMIG,
257+
GPFS_DMAPI_XATTR_TAPE_TPS,
258+
};
247259
const static std::vector<std::string> USER_XATTRS{
248260
"user.content_type",
249261
"user.content_md5",
@@ -286,8 +298,7 @@ build_gpfs_get_ea_request(gpfsRequest_t* reqP, std::string key)
286298
reqP->payload.structLen = reqP->header.totalLength - sizeof(reqP->header);
287299
reqP->payload.structType = GPFS_FCNTL_GET_XATTR;
288300
reqP->payload.nameLen = nameLen;
289-
// bufferLen is the size of buffer - roundingup of the attribute name to 8 chars
290-
reqP->payload.bufferLen = bufLen - ROUNDUP(nameLen, 8);
301+
reqP->payload.bufferLen = bufLen - nameLen;
291302
reqP->payload.flags = GPFS_FCNTL_XATTRFLAG_NONE;
292303
memcpy(&reqP->payload.buffer[0], key.c_str(), nameLen);
293304
}
@@ -461,9 +472,14 @@ get_fd_xattr(int fd, XattrMap& xattr, const std::vector<std::string>& xattr_keys
461472
}
462473

463474
static int
464-
get_fd_gpfs_xattr(int fd, XattrMap& xattr, int& gpfs_error)
475+
get_fd_gpfs_xattr(int fd, XattrMap& xattr, int& gpfs_error, bool use_dmapi)
465476
{
466-
for (auto const& key : GPFS_XATTRS) {
477+
auto gpfs_xattrs { GPFS_XATTRS };
478+
if (use_dmapi) {
479+
gpfs_xattrs.insert(gpfs_xattrs.end(), GPFS_DMAPI_XATTRS.begin(), GPFS_DMAPI_XATTRS.end());
480+
}
481+
482+
for (auto const& key : gpfs_xattrs) {
467483
gpfsRequest_t gpfsGetXattrRequest;
468484
build_gpfs_get_ea_request(&gpfsGetXattrRequest, key);
469485
int r = dlsym_gpfs_fcntl(fd, &gpfsGetXattrRequest);
@@ -472,7 +488,7 @@ get_fd_gpfs_xattr(int fd, XattrMap& xattr, int& gpfs_error)
472488
if (gpfs_error == GPFS_FCNTL_ERR_NONE) {
473489
int name_len = gpfsGetXattrRequest.payload.nameLen;
474490
int buffer_len = gpfsGetXattrRequest.payload.bufferLen;
475-
xattr[key] = std::string(gpfsGetXattrRequest.buffer[ROUNDUP(name_len, 8)], buffer_len);
491+
xattr[key] = std::string((char*)gpfsGetXattrRequest.buffer + name_len, buffer_len);
476492
} else if (gpfs_error != GPFS_FCNTL_ERR_NO_ATTR) {
477493
LOG("get_fd_gpfs_xattr: get GPFS xattr with fcntl failed with error." << DVAL(gpfs_error));
478494
return gpfs_error;
@@ -603,6 +619,8 @@ struct FSWorker : public Napi::AsyncWorker
603619
// NOTE: If _do_ctime_check = false, then some functions will fallback to using mtime check
604620
bool _do_ctime_check;
605621

622+
bool _use_dmapi;
623+
606624
FSWorker(const Napi::CallbackInfo& info)
607625
: AsyncWorker(info.Env())
608626
, _deferred(Napi::Promise::Deferred::New(info.Env()))
@@ -616,6 +634,7 @@ struct FSWorker : public Napi::AsyncWorker
616634
, _should_add_thread_capabilities(false)
617635
, _supplemental_groups()
618636
, _do_ctime_check(false)
637+
, _use_dmapi(false)
619638
{
620639
for (int i = 0; i < (int)info.Length(); ++i) _args_ref.Set(i, info[i]);
621640
if (info[0].ToBoolean()) {
@@ -635,6 +654,7 @@ struct FSWorker : public Napi::AsyncWorker
635654
_report_fs_stats = Napi::Persistent(fs_context.Get("report_fs_stats").As<Napi::Function>());
636655
}
637656
_do_ctime_check = fs_context.Get("do_ctime_check").ToBoolean();
657+
_use_dmapi = fs_context.Get("use_dmapi").ToBoolean();
638658
}
639659
}
640660
void Begin(std::string desc)
@@ -793,7 +813,7 @@ struct Stat : public FSWorker
793813
if (!_use_lstat) {
794814
SYSCALL_OR_RETURN(get_fd_xattr(fd, _xattr, _xattr_get_keys));
795815
if (use_gpfs_lib()) {
796-
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error));
816+
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error, _use_dmapi));
797817
}
798818
}
799819

@@ -1221,7 +1241,7 @@ struct Readfile : public FSWorker
12211241
if (_read_xattr) {
12221242
SYSCALL_OR_RETURN(get_fd_xattr(fd, _xattr, _xattr_get_keys));
12231243
if (use_gpfs_lib()) {
1224-
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error));
1244+
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error, _use_dmapi));
12251245
}
12261246
}
12271247

@@ -1752,7 +1772,7 @@ struct FileStat : public FSWrapWorker<FileWrap>
17521772
SYSCALL_OR_RETURN(fstat(fd, &_stat_res));
17531773
SYSCALL_OR_RETURN(get_fd_xattr(fd, _xattr, _xattr_get_keys));
17541774
if (use_gpfs_lib()) {
1755-
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error));
1775+
GPFS_FCNTL_OR_RETURN(get_fd_gpfs_xattr(fd, _xattr, gpfs_error, _use_dmapi));
17561776
}
17571777

17581778
if (_do_ctime_check) {

0 commit comments

Comments
 (0)