mirror of
https://github.com/HeyPuter/puter
synced 2024-11-14 22:06:00 +00:00
dev: apply TTL cache for stat entries
Adds CachedFilesystem layer to client filesystem chain. Currently only stat is implemented. The stat implementation will hold onto an entry for 3 seconds as per hardcoded configuration. Eventually, once invalidation via websockets is working, this TTL should be extended.
This commit is contained in:
parent
52d5c4706c
commit
76e64249a6
124
src/puter-js/src/modules/FileSystem/CacheFS.js
Normal file
124
src/puter-js/src/modules/FileSystem/CacheFS.js
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import putility from "@heyputer/putility";
|
||||||
|
import { RWLock } from "@heyputer/putility/src/libs/promise";
|
||||||
|
import { ProxyFilesystem, TFilesystem } from "./definitions";
|
||||||
|
|
||||||
|
export const ROOT_UUID = '00000000-0000-0000-0000-000000000000';
|
||||||
|
|
||||||
|
export class CacheFS extends putility.AdvancedBase {
|
||||||
|
static PROPERTIES = {
|
||||||
|
// 'internal_uuid' maps a path or external UUID to
|
||||||
|
// the respective cache entry UUID
|
||||||
|
// (which for now is the same as the public UUID)
|
||||||
|
internal_uuid: () => ({}),
|
||||||
|
entries: () => ({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
get_entry_ei (external_identifier) {
|
||||||
|
const internal_identifier = this.internal_uuid[external_identifier];
|
||||||
|
if ( ! internal_identifier ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return this.entries[internal_identifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
add_entry ({
|
||||||
|
external_identifiers,
|
||||||
|
internal_identifier,
|
||||||
|
}) {
|
||||||
|
const entry = {
|
||||||
|
stat_has: {},
|
||||||
|
stat_exp: 0,
|
||||||
|
locks: {
|
||||||
|
stat: new RWLock(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
for ( const ident of external_identifiers ) {
|
||||||
|
this.internal_uuid[ident] = internal_identifier;
|
||||||
|
}
|
||||||
|
this.entries[internal_identifier] = entry;
|
||||||
|
console.log('cREATED ENTRY', this.internal_uuid, this.entries);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CachedFilesystem extends ProxyFilesystem {
|
||||||
|
constructor (o) {
|
||||||
|
super(o);
|
||||||
|
// this.cacheFS = cacheFS;
|
||||||
|
this.cacheFS = new CacheFS();
|
||||||
|
}
|
||||||
|
static IMPLEMENTS = {
|
||||||
|
[TFilesystem]: {
|
||||||
|
stat: async function (o) {
|
||||||
|
let cent = this.cacheFS.get_entry_ei(o.path ?? o.uid);
|
||||||
|
|
||||||
|
const modifiers = [
|
||||||
|
'subdomains',
|
||||||
|
'permissions',
|
||||||
|
'versions',
|
||||||
|
'size',
|
||||||
|
];
|
||||||
|
|
||||||
|
let values_requested = {};
|
||||||
|
for ( const mod of modifiers ) {
|
||||||
|
const optionsKey = 'return' +
|
||||||
|
mod.charAt(0).toUpperCase() +
|
||||||
|
mod.slice(1);
|
||||||
|
if ( ! o[optionsKey] ) continue;
|
||||||
|
values_requested[mod] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const satisfactory_cache = cent => {
|
||||||
|
for ( const mod of modifiers ) {
|
||||||
|
if ( ! values_requested[mod] ) continue;
|
||||||
|
if ( ! cent.stat_has[mod] ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cached_stat;
|
||||||
|
if ( cent && cent.stat && cent.stat_exp > Date.now() ) {
|
||||||
|
const l = await cent.locks.stat.rlock();
|
||||||
|
if ( satisfactory_cache(cent) ) {
|
||||||
|
cached_stat = cent.stat;
|
||||||
|
}
|
||||||
|
l.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cached_stat ) {
|
||||||
|
console.log('CACHE HIT');
|
||||||
|
return cached_stat;
|
||||||
|
}
|
||||||
|
console.log('CACHE MISS');
|
||||||
|
|
||||||
|
let l;
|
||||||
|
if ( cent ) {
|
||||||
|
l = await cent.locks.stat.wlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DOING THE STAT', o);
|
||||||
|
const entry = await this.delegate.stat(o);
|
||||||
|
|
||||||
|
if ( ! cent ) {
|
||||||
|
cent = this.cacheFS.add_entry({
|
||||||
|
external_identifiers: [entry.path, entry.uid],
|
||||||
|
internal_identifier: entry.uid,
|
||||||
|
});
|
||||||
|
l = await cent.locks.stat.wlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
cent.stat = entry;
|
||||||
|
cent.stat_has = { ...values_requested };
|
||||||
|
// TODO: increase cache TTL once invalidation works
|
||||||
|
cent.stat_exp = Date.now() + 1000*3;
|
||||||
|
|
||||||
|
l.unlock();
|
||||||
|
|
||||||
|
console.log('RETRUNING THE ENTRY', entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import sign from "./operations/sign.js";
|
|||||||
import deleteFSEntry from "./operations/deleteFSEntry.js";
|
import deleteFSEntry from "./operations/deleteFSEntry.js";
|
||||||
import { ProxyFilesystem, PuterAPIFilesystem, TFilesystem } from './definitions.js';
|
import { ProxyFilesystem, PuterAPIFilesystem, TFilesystem } from './definitions.js';
|
||||||
import { AdvancedBase } from '../../../../putility/index.js';
|
import { AdvancedBase } from '../../../../putility/index.js';
|
||||||
|
import { CachedFilesystem } from './CacheFS.js';
|
||||||
|
|
||||||
export class PuterJSFileSystemModule extends AdvancedBase {
|
export class PuterJSFileSystemModule extends AdvancedBase {
|
||||||
|
|
||||||
@ -34,12 +35,14 @@ export class PuterJSFileSystemModule extends AdvancedBase {
|
|||||||
static NARI_METHODS = {
|
static NARI_METHODS = {
|
||||||
stat: {
|
stat: {
|
||||||
positional: ['path'],
|
positional: ['path'],
|
||||||
fn (parameters) {
|
firstarg_options: true,
|
||||||
|
async fn (parameters) {
|
||||||
return this.filesystem.stat(parameters);
|
return this.filesystem.stat(parameters);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
readdir: {
|
readdir: {
|
||||||
positional: ['path'],
|
positional: ['path'],
|
||||||
|
firstarg_options: true,
|
||||||
fn (parameters) {
|
fn (parameters) {
|
||||||
return this.filesystem.readdir(parameters);
|
return this.filesystem.readdir(parameters);
|
||||||
}
|
}
|
||||||
@ -75,6 +78,7 @@ export class PuterJSFileSystemModule extends AdvancedBase {
|
|||||||
|
|
||||||
// Construct the decorator chain for the client-side filesystem.
|
// Construct the decorator chain for the client-side filesystem.
|
||||||
let fs = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
|
let fs = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
|
||||||
|
fs = new CachedFilesystem({ delegate: fs }).as(TFilesystem);
|
||||||
fs = new ProxyFilesystem({ delegate: fs }).as(TFilesystem);
|
fs = new ProxyFilesystem({ delegate: fs }).as(TFilesystem);
|
||||||
this.filesystem = fs;
|
this.filesystem = fs;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user