mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-30 17:50:00 -06:00
Cache is king baby! Let's go 🚀 (#1574)
* Implement the first naive version of `readdir` cache * Purge the entire cache on every single mutation Right now we're going to use the very naive, but safe, approach to purge the entire cache whenever there is change in the user's fs. We're going to incrementally improve this; but for now, better safe than sorry! * Add socket event listeners to flush cache on file system item changes This update introduces event listeners for 'item.added', 'item.renamed', and 'item.moved' events, triggering a cache flush on each event to ensure data consistency in the file system module. * increase exp time for the cache * Update readdir.js * Update index.js
This commit is contained in:
9
package-lock.json
generated
9
package-lock.json
generated
@@ -2278,9 +2278,9 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@heyputer/kv.js": {
|
||||
"version": "0.1.91",
|
||||
"resolved": "https://registry.npmjs.org/@heyputer/kv.js/-/kv.js-0.1.91.tgz",
|
||||
"integrity": "sha512-TzgPFVicgaxkz4mIavE8UdfICQ2Oql9BWkFlJAWEQ9cl+EXWmV1f7sQ0NRJxhzLahfVUdgoWsTXqx/ndr/9KBg==",
|
||||
"version": "0.1.92",
|
||||
"resolved": "https://registry.npmjs.org/@heyputer/kv.js/-/kv.js-0.1.92.tgz",
|
||||
"integrity": "sha512-D+trimrG/V6mU5zeQrKyH476WotvvRn0McttxiFxEzWLiMqR6aBmQ5apeKrZAheglHmwf0D3FO5ykmU2lCuLvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimatch": "^9.0.0"
|
||||
@@ -20419,6 +20419,9 @@
|
||||
"name": "@heyputer/puterjs",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@heyputer/kv.js": "^0.1.92"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
|
||||
@@ -954,7 +954,7 @@ async function UIDesktop(options) {
|
||||
{
|
||||
html: i18n('refresh'),
|
||||
onClick: function () {
|
||||
refresh_item_container(el_desktop);
|
||||
refresh_item_container(el_desktop, { consistency: 'strong' });
|
||||
}
|
||||
},
|
||||
// -------------------------------------------
|
||||
|
||||
@@ -2340,7 +2340,10 @@ async function UIWindow(options) {
|
||||
menu_items.push({
|
||||
html: i18n('refresh'),
|
||||
onClick: function(){
|
||||
refresh_item_container(el_window_body, options);
|
||||
refresh_item_container(el_window_body, {
|
||||
...options,
|
||||
consistency: 'strong',
|
||||
});
|
||||
}
|
||||
})
|
||||
// -------------------------------------------
|
||||
|
||||
@@ -108,7 +108,7 @@ const refresh_item_container = function(el_item_container, options){
|
||||
$(el_item_container).find('.item').removeItems()
|
||||
|
||||
// get items
|
||||
puter.fs.readdir(container_path).then((fsentries)=>{
|
||||
puter.fs.readdir({path: container_path, consistency: options.consistency }).then((fsentries)=>{
|
||||
// Check if the same folder is still loading since el_item_container's
|
||||
// data-path might have changed by other operations while waiting for the response to this `readdir`.
|
||||
if($(el_item_container).attr('data-path') !== container_path)
|
||||
|
||||
@@ -16,5 +16,8 @@
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heyputer/kv.js": "^0.1.92"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { FilesystemService } from './services/Filesystem.js';
|
||||
import { FSRelayService } from './services/FSRelay.js';
|
||||
import { NoPuterYetService } from './services/NoPuterYet.js';
|
||||
import { XDIncomingService } from './services/XDIncoming.js';
|
||||
import kvjs from '@heyputer/kv.js';
|
||||
|
||||
// TODO: This is for a safe-guard below; we should check if we can
|
||||
// generalize this behavior rather than hard-coding it.
|
||||
@@ -115,6 +116,9 @@ export default globalThis.puter = (function() {
|
||||
constructor(options) {
|
||||
options = options ?? {};
|
||||
|
||||
// Initialize the cache using kv.js
|
||||
this._cache = new kvjs();
|
||||
|
||||
// "modules" in puter.js are external interfaces for the developer
|
||||
this.modules_ = [];
|
||||
// "services" in puter.js are used by modules and may interact with each other
|
||||
|
||||
@@ -104,6 +104,19 @@ export class PuterJSFileSystemModule extends AdvancedBase {
|
||||
}
|
||||
|
||||
bindSocketEvents() {
|
||||
this.socket.on('item.added', (item) => {
|
||||
// todo: NAIVE PURGE
|
||||
puter._cache.flushall();
|
||||
});
|
||||
this.socket.on('item.renamed', (item) => {
|
||||
// todo: NAIVE PURGE
|
||||
puter._cache.flushall();
|
||||
});
|
||||
this.socket.on('item.moved', (item) => {
|
||||
// todo: NAIVE PURGE
|
||||
puter._cache.flushall();
|
||||
});
|
||||
|
||||
this.socket.on('connect', () => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Connected', this.socket.id);
|
||||
@@ -112,6 +125,10 @@ export class PuterJSFileSystemModule extends AdvancedBase {
|
||||
this.socket.on('disconnect', () => {
|
||||
if(puter.debugMode)
|
||||
console.log('FileSystem Socket: Disconnected');
|
||||
|
||||
// todo: NAIVE PURGE
|
||||
// purge cache on disconnect since we may have become out of sync
|
||||
puter._cache.flushall();
|
||||
});
|
||||
|
||||
this.socket.on('reconnect', (attempt) => {
|
||||
|
||||
@@ -55,6 +55,9 @@ const copy = function (...args) {
|
||||
// if user is copying an item to where its source is, change the name so there is no conflict
|
||||
dedupe_name: (options.dedupe_name || options.dedupeName),
|
||||
}));
|
||||
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ const mkdir = function (...args) {
|
||||
original_client_socket_id: this.socket.id,
|
||||
create_missing_parents: (options.recursive || options.createMissingParents) ?? false,
|
||||
}));
|
||||
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ const move = function (...args) {
|
||||
new_metadata: (options.new_metadata || options.newMetadata),
|
||||
original_client_socket_id: options.excludeSocketID,
|
||||
}));
|
||||
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,33 @@ const readdir = async function (...args) {
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// consistency levels
|
||||
if(!options.consistency){
|
||||
options.consistency = 'strong';
|
||||
}
|
||||
|
||||
// Either path or uid is required
|
||||
if(!options.path && !options.uid){
|
||||
throw new Error({ code: 'NO_PATH_OR_UID', message: 'Either path or uid must be provided.' });
|
||||
}
|
||||
|
||||
// Generate cache key based on path or uid
|
||||
let cacheKey;
|
||||
if(options.path){
|
||||
cacheKey = 'readdir:' + options.path;
|
||||
}else if(options.uid){
|
||||
cacheKey = 'readdir:' + options.uid;
|
||||
}
|
||||
|
||||
if(options.consistency === 'eventual'){
|
||||
// Check cache
|
||||
const cachedResult = await puter._cache.get(cacheKey);
|
||||
if(cachedResult){
|
||||
resolve(cachedResult);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If auth token is not provided and we are in the web environment,
|
||||
// try to authenticate with Puter
|
||||
if(!puter.authToken && puter.env === 'web'){
|
||||
@@ -40,7 +62,21 @@ const readdir = async function (...args) {
|
||||
const xhr = utils.initXhr('/readdir', this.APIOrigin, this.authToken);
|
||||
|
||||
// set up event handlers for load and error events
|
||||
utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
|
||||
utils.setupXhrEventHandlers(xhr, options.success, options.error, async (result) => {
|
||||
// Calculate the size of the result for cache eligibility check
|
||||
const resultSize = JSON.stringify(result).length;
|
||||
|
||||
// Cache the result if it's not bigger than MAX_CACHE_SIZE
|
||||
const MAX_CACHE_SIZE = 20 * 1024 * 1024;
|
||||
const EXPIRE_TIME = 30;
|
||||
|
||||
if(resultSize <= MAX_CACHE_SIZE){
|
||||
// UPSERT the cache
|
||||
await puter._cache.set(cacheKey, result, { EX: EXPIRE_TIME });
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
}, reject);
|
||||
|
||||
// Build request payload - support both path and uid parameters
|
||||
const payload = {
|
||||
|
||||
@@ -50,6 +50,8 @@ const rename = function (...args) {
|
||||
}
|
||||
|
||||
xhr.send(JSON.stringify(dataToSend));
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -429,6 +429,9 @@ const upload = async function(items, dirPath, options = {}){
|
||||
options.start();
|
||||
}
|
||||
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
|
||||
// send request
|
||||
xhr.send(fd);
|
||||
})
|
||||
|
||||
@@ -55,6 +55,9 @@ const write = async function (targetPath, data, options = {}) {
|
||||
throw new Error({ code: 'field_invalid', message: 'write() data parameter is an invalid type' });
|
||||
}
|
||||
|
||||
// todo: EXTREMELY NAIVE CACHE PURGE
|
||||
puter._cache.flushall();
|
||||
|
||||
// perform upload
|
||||
return this.upload(data, parent, options);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ import webpack from 'webpack';
|
||||
import { fileURLToPath } from 'url';
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
console.log('ENV CHECK!!!', process.env.PUTER_ORIGIN, process.env.PUTER_API_ORIGIN);
|
||||
|
||||
export default {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
|
||||
Reference in New Issue
Block a user