fix(fs) optimize stat operation. (#2649)

This commit is contained in:
Eric Dubé
2026-03-11 20:18:42 -04:00
committed by GitHub
parent 7ff1ecfcd7
commit 9a78b6bffa
2 changed files with 101 additions and 46 deletions
+31 -21
View File
@@ -69,6 +69,8 @@ module.exports = class FSNodeContext {
NodePathSelector,
];
#writable;
/**
* Creates an instance of FSNodeContext.
* @param {*} opt_identifier
@@ -401,30 +403,35 @@ module.exports = class FSNodeContext {
this.entry.subdomains = [];
this.entry.workers = [];
let subdomains = await db.read('SELECT * FROM subdomains WHERE root_dir_id = ? AND user_id = ?',
[this.entry.id, user.id]);
let subdomains = await db.read(
'SELECT * FROM subdomains WHERE root_dir_id = ? AND user_id = ?',
[this.entry.id, user.id],
);
if ( subdomains.length > 0 ) {
subdomains.forEach((sd) => {
if ( this.entry.is_dir ) {
this.entry.subdomains.push({
subdomain: sd.subdomain,
address: `${config.protocol }://${ sd.subdomain }.` + 'puter.site',
uuid: sd.uuid,
});
} else {
const workerName = sd.subdomain.split('.').pop();
this.entry.workers.push({
subdomain: workerName,
address: `https://${ workerName }.` + 'puter.work',
uuid: sd.uuid,
});
}
this.applySingleSubdomain(sd);
});
this.entry.has_website = true;
}
}
applySingleSubdomain (sd) {
if ( this.entry.is_dir ) {
this.entry.subdomains.push({
subdomain: sd.subdomain,
address: `${config.protocol }://${ sd.subdomain }.` + 'puter.site',
uuid: sd.uuid,
});
} else {
const workerName = sd.subdomain.split('.').pop();
this.entry.workers.push({
subdomain: workerName,
address: `https://${ workerName }.` + 'puter.work',
uuid: sd.uuid,
});
}
}
/**
* Fetches the owner of a directory or file and stores it on the
* `owner` property of the fsentry.
@@ -535,8 +542,10 @@ module.exports = class FSNodeContext {
const db = this.services.get('database').get(DB_READ, 'filesystem');
let versions = await db.read('SELECT * FROM fsentry_versions WHERE fsentry_id = ?',
[this.entry.id]);
let versions = await db.read(
'SELECT * FROM fsentry_versions WHERE fsentry_id = ?',
[this.entry.id],
);
const versions_tidy = [];
for ( const version of versions ) {
let username = version.user_id ? (await get_user({ id: version.user_id })).username : null;
@@ -599,7 +608,7 @@ module.exports = class FSNodeContext {
await this.fetchIsEmpty();
}
async get (key) {
async get (key, force) {
/*
This isn't supposed to stay like this!
@@ -719,10 +728,11 @@ module.exports = class FSNodeContext {
}
if ( key === 'writable' ) {
if ( this.#writable && !force ) return this.#writable;
const actor = Context.get('actor');
if ( !actor || !actor.type.user ) return undefined;
const svc_acl = this.services.get('acl');
return await svc_acl.check(actor, this, 'write');
return this.#writable = await svc_acl.check(actor, this, 'write');
}
throw new Error(`unrecognize key for FSNodeContext.get: ${key}`);
@@ -54,41 +54,86 @@ class HLStat extends HLFilesystemOperation {
const user_unix_ts = Number((`${Date.parse(Context.get('actor')?.type?.user?.timestamp)}`).slice(0, -3));
const paths_are_fine = user_unix_ts >= 1722385593;
if ( maybe_uid_selector || paths_are_fine ) {
// We are able to fetch the entry and is_empty simultaneously
await Promise.all([
subject.fetchEntry(),
subject.fetchIsEmpty(),
]);
} else {
// We need the entry first in order for is_empty to work correctly
await subject.fetchEntry();
await subject.fetchIsEmpty();
const do_after_fetchEntry = [];
const do_alongside_fetchEntry = [];
if ( return_size ) {
do_after_fetchEntry.push(async () => {
await subject.fetchSize(user);
});
}
if ( return_subdomains ) {
do_after_fetchEntry.push(async () => {
await subject.fetchSubdomains(user);
});
}
if ( return_shares || return_permissions ) {
do_after_fetchEntry.push(async () => {
await subject.fetchShares();
});
}
if ( return_versions ) {
do_after_fetchEntry.push(async () => {
await subject.fetchVersions();
});
}
do_after_fetchEntry.push(async () => {
await subject.fetchOwner();
}, async () => {
await subject.get('writable');
});
((maybe_uid_selector || paths_are_fine)
? do_alongside_fetchEntry
: do_after_fetchEntry).push(subject.fetchIsEmpty.bind(subject));
// if ( maybe_uid_selector || paths_are_fine ) {
// await Promise.all([
// subject.fetchEntry(),
// subject.fetchIsEmpty(),
// ]);
// } else {
// // We need the entry first in order for is_empty to work correctly
// await subject.fetchEntry();
// await subject.fetchIsEmpty();
// }
await Promise.all([
(async () => {
await subject.fetchEntry();
const context = Context.get();
const svc_acl = context.get('services').get('acl');
const actor = context.get('actor');
if ( ! await svc_acl.check(actor, subject, 'read') ) {
throw await svc_acl.get_safe_acl_error(actor, subject, 'read');
}
if ( ! subject.found ) {
throw APIError.create('subject_does_not_exist');
}
await Promise.all(do_after_fetchEntry.map(f => f()));
})(),
...(do_alongside_fetchEntry.map(f => f())),
]);
// file not found
if ( ! subject.found ) throw APIError.create('subject_does_not_exist');
await subject.fetchOwner();
const context = Context.get();
const svc_acl = context.get('services').get('acl');
const actor = context.get('actor');
if ( ! await svc_acl.check(actor, subject, 'read') ) {
throw await svc_acl.get_safe_acl_error(actor, subject, 'read');
}
// await subject.fetchOwner();
// TODO: why is this specific to stat?
const mime = this.require('mime-types');
const contentType = mime.contentType(subject.entry.name);
subject.entry.type = contentType ? contentType : null;
if ( return_size ) await subject.fetchSize(user);
if ( return_subdomains ) await subject.fetchSubdomains(user);
if ( return_shares || return_permissions ) {
await subject.fetchShares();
}
if ( return_versions ) await subject.fetchVersions();
// if ( return_size ) await subject.fetchSize(user);
// if ( return_subdomains ) await subject.fetchSubdomains(user);
// if ( return_shares || return_permissions ) {
// await subject.fetchShares();
// }
// if ( return_versions ) await subject.fetchVersions();
return await subject.getSafeEntry();
}