Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to use the IndexedDB backend in a worker ? #91

Closed
robert-westenberger opened this issue Jul 23, 2024 · 7 comments · Fixed by #93
Closed

Is it possible to use the IndexedDB backend in a worker ? #91

robert-westenberger opened this issue Jul 23, 2024 · 7 comments · Fixed by #93
Assignees
Labels
bug Something isn't working
Milestone

Comments

@robert-westenberger
Copy link

In my main thread, I can configure zenfs to access a remote filesystem running in a webworker in a different context.

When using the InMemory backend, everything works as expected.

// worker.ts
import { attachFS, resolveMountConfig, InMemory } from '@zenfs/core';

const myFS = await resolveMountConfig({ backend: InMemory, name: 'foo' });
attachFS(self, myFS);
// main.ts
import { configure, Port, fs } from '@zenfs/core';
const worker = new Worker(new URL('./worker.ts', import.meta.url), {
      type: 'module',
    });

await configure({
  mounts: {
    '/port': {
      backend: Port,
      port: worker,
    },
  },
});

// Can now use methods on fs to interact with filesystem running in web worker thread..

But if I try to use the IndexedDB backend in the worker, i get an error.

// worker.ts
import { attachFS, resolveMountConfig } from '@zenfs/core';
import {IndexedDB} from "@zenfs/dom";

const myFS = await resolveMountConfig({ backend: IndexedDB, name: 'foo' });
attachFS(self, myFS);

The error is Error: RPC Failed,this line:

const error = new ErrnoError(Errno.EIO, 'RPC Failed');

I have a feeling that I am not using the Port backend as its intended, apologies in advance if that is the case!

@james-pre
Copy link
Member

@robert-westenberger Your code looks to be correct. Perhaps something in the worker fails? I suggest you check that indexedDB is available. If it is, then I'm not entirely sure what would cause a failure in the worker. Feel free to reach out if you have any questions.

@james-pre james-pre self-assigned this Jul 23, 2024
@james-pre james-pre added the bug Something isn't working label Jul 23, 2024
@james-pre james-pre added this to the 1.0 milestone Jul 23, 2024
@UnCor3
Copy link
Contributor

UnCor3 commented Jul 24, 2024

@robert-westenberger Put your worker code in try catch block , in my case isomorphic-git was erroring out here is how i got it working without isomorphic-git

//worker.ts
import { attachFS, resolveMountConfig, fs } from "@zenfs/core";
import { IndexedDB } from "@zenfs/dom";

try {
    const myFS = await resolveMountConfig({ backend: IndexedDB });
    attachFS(self as any, myFS);
    
    //passed an empty object
    myFS.mkdirSync("/mnt", 1, {});

    //didn't work
    // await git.clone({
    //   fs: myFS as any,
    //   http,
    //   dir: "/",
    //   url: "https://github.com/isomorphic-git/lightning-fs",
    //   corsProxy: "https://cors.isomorphic-git.org",
    // });
} catch (error) {
    console.error(error);
}
//main.ts

import { configure, Port, fs } from "@zenfs/core";
import useWorkerStore from "@/store/worker.store";
export class GitWorkerIPC {
  public workerStore = useWorkerStore();
  public constructor() {
    this.init();
  }
  public async init() {
    try {
      await configure({
        mounts: {
          "/port": {
            backend: Port,
            //GitWorker is a Worker instance
            port: this.workerStore.GitWorker,
          },
        },
      });
        console.log(fs.readdirSync("/port")); //logs ["mnt"]
    } catch (error) {
        console.error(error);
    }
  }
}

@james-pre This is what fails to execute in isomorphic-git , most likely fs returned from resolveMountConfig misses properties ,its working fine if i do not use the Port backend and instead use Indexeddb backend on worker thread

Is Port backend fs missing some methods ? @james-pre
see it in isomorphic-git source code

function bindFs(target, fs) {
  if (isPromiseFs(fs)) {
    for (const command of commands) {
    //TypeError: fs[command] is undefined
      target[`_${command}`] = fs[command].bind(fs);
    }
  } else {
    for (const command of commands) {
      target[`_${command}`] = pify(fs[command].bind(fs));
    }
  }

@james-pre
Copy link
Member

james-pre commented Jul 24, 2024

@UnCor3,

@james-pre This is what fails to execute in isomorphic-git , most likely fs returned from resolveMountConfig misses properties ,its working fine if i do not use the Port backend and instead use Indexeddb backend on worker thread

If you look at the Mounting and unmounting, creating backends section of the readme, you will find a very clear notice:

Warning

Instances of backends follow the internal ZenFS API. You should never use a backend's methods unless you are extending a backend.

I think this should answer your question. The internal API (FileSystem) is very different from the node:fs API. You should not be passing the result of resolveMountConfig to 3rd party APIs that expect a node:fs object. Instead, you should pass the fs exported by @zenfs/core. For example:

//worker.ts
import { attachFS, resolveMountConfig, fs } from '@zenfs/core';
import { IndexedDB } from '@zenfs/dom';

try {
	const myFS = await resolveMountConfig({ backend: IndexedDB });
	attachFS(self, myFS); // If a type cast is needed here please open another issue

	await git.clone({
		fs, // notice we are using the named `fs` export
		http, // assuming you've imported this
		dir: '/',
		url: 'https://github.com/isomorphic-git/lightning-fs',
		corsProxy: 'https://cors.isomorphic-git.org/',
	});
} catch (error) {
	console.error(error);
}

However, the IndexedDB backend is not mounted. I've found mounting a backend and using it with a Port sometimes does not work, so mounting it on the worker's fs may cause issues.

My suggestion for fixing your problem:

  1. Use isomorphic-git from the main thread, which has the fs with a mounted Port, or
  2. Mount the backend on the fs in the worker

Example worker for #​2:

	// ...
	const idbfs = await resolveMountConfig({ backend: IndexedDB });
	attachFS(self, idbfs); // If a type cast is needed here please open another issue
	fs.umount('/'); // unmounting the default in-memory mount on /
	fs.mount('/', idbfs);
	// ...

or

	// ...
	await configure({
		mounts: {
			/* Note: this is a convenience thing, `{ backend: IndexedDB }` works too.
			You can't pass options in this form */
			'/': IndexedDB 
		}
	});
	attachFS(self, fs.mounts.get('/')); // If a type cast is needed here please open another issue
	// ...

@robert-westenberger
Copy link
Author

@james-pre When I console.log self.indexedDB in the worker, it is available.

Here is a reproduction https://stackblitz.com/edit/vitejs-vite-grnn2s?file=src%2Fworker.ts

@james-pre
Copy link
Member

james-pre commented Jul 25, 2024

@robert-westenberger,

I cleaned up the RPC types in 0.16.0 and also changed the dependency range for @zenfs/core in @zenfs/dom 0.2.14 to be >= instead of ^, which means you can actually use core v0.13+ with it. Please let me know if upgrading both packages fixes the issue.

Thanks.

@robert-westenberger
Copy link
Author

@james-pre Thank you for looking at this. I've updated core to 0.16.0 and dom to 0.2.14 and get the same issue. I've updated the reproduction:

https://stackblitz.com/edit/vitejs-vite-grnn2s?file=src%2Fworker.ts

@UnCor3
Copy link
Contributor

UnCor3 commented Jul 27, 2024

@james-pre Yesterday, i started having this issue randomly and tried my best to fix the issue CI also passed can you check my pr #92 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
3 participants