Open bitmage opened 4 years ago
Maybe with something like this? https://github.com/tomaka/wasm-timer
@olanod Thanks for chiming in! It looks like you put a lot of work into this. I don't feel qualified to evaluate it to be honest. I'd be interested to see a writeup of the implementation decisions that led to the current form.
Maybe @spacejam or one of the other contributors has more to say.
No worries, but credit where it's due, @tomaka is the author and he would know better about the troubles of porting this kind of libraries to WASM. I just happen be a stalker that knows about its existence because aside from sled I'm building stuff with libp2p and substrate(projects he works on) which is not trivial at all software that I believe can already run in the browser probably thanks to that library.
As a sled user I'd be cool to see it running in the browser but I'm not sure it's practical to implement it on top of something like the storage API, what I see substrate did was to to create their on facade with reduced API https://crates.io/crates/kvdb and on native uses a rocksdb backend and in the web an indexeddb one, sled could be one of those backends for example. If the goal is using only sled(not abstractions on top) maybe the internals of sled have some abstractions for the file system that can be replaced by an indexeddb/localstorage implementation but that might just be implementing a new sled leaving only its API. In the future might be more interesting to see WASI support in the browser, if it doesn't already, sled should be easy to make it work on WASI since the MVP was all about dealing with file descriptors. Another interesting option would be the native file system API which is a chrome experiment for now.
A fun thing to try that might work with today's tech with experiment-only quality could be to compile sled to WASI and have a WASI polyfill that uses that native file system API :thinking:
For what it's worth, my wasm-timer
, while fully-working, is not meant to be a proper long-term solution.
In my opinion, there are only two possible ways to properly handle the time in a WASM environment:
std::time::Instant
or SystemTime
at all, and let users inject a value representing the current time via some API function. This would likely lead to a cumbersome API.wasm32-wasi
, where std::time
works, rather than wasm32-unknown-unknown
.I believe wasm32-wasi
is the preferred way to go forward with this, at least initially. Using https://github.com/bytecodealliance/cargo-wasi I'm able to build but running currently hits this issue:
λ cargo +nightly wasi run --release
...
Compiling struct_sled v0.1.0 (/tmp/struct_sled)
Finished release [optimized] target(s) in 12.97s
Running `/home/t/.cargo/bin/cargo-wasi target/wasm32-wasi/release/struct_sled.wasm`
Optimizing with wasm-opt
Downloading precompiled wasm-opt version_92
Running `target/wasm32-wasi/release/struct_sled.wasm`
error while processing main module target/wasm32-wasi/release/struct_sled.wasm: Instantiation error: Link error: wasi_snapshot_preview1/proc_exit: no provided import function
I feel that wasm is in general a high priority long-term goal, and if anyone is able to dig in more I'd love to see this working better. I need to spend a bit more energy on reliability efforts for the short term.
I've been traveling the whole summer. Tried to hack on this a couple of times... I'm back home now with more time to dedicate to it, but still not making progress.
@spacejam in your example above it seems like you have created a demo project to test sled with. I tried to replicate with this example but I get a panic on getpid()
.
Ideally I would like to run the tests instead, so I can stay in sled source code and make changes as necessary. By running the command cargo +nightly wasi test --features testing
I get 1: unknown import: 'env::now' has not been defined
.
From what I can find out about the WASM spec, the imports of a module are known statically, and compiled into a prelude or header at the beginning of the wasm file. I believe the attempt to satisfy the dependencies happens before any of the body of WASM code is run, which explains why there is no stack trace on an unknown import
. From Rust, the way WASM imports are
declared is with a #[link(wasm_import_module = "the-wasm-import-module")
and an optional #[link_name = "bar"]
on the function to be imported. I don't see code like this anywhere in the Rust WASI source, so I wonder how this is implemented.
I can see that getpid() is implemented for WASI in the rust source as panic!("unsupported")
, which explains the behavior I see in my sled-wasi
project.
It looks like Instant::now()
and SystemTime::now()
are implemented in Rust WASI, so I'm scratching my head as to why the tests fail on this. The import being attempted is env::now
, which is different than the calls that any of the sled code is making. I assume that somewhere within the Rust WASI implementation the imports are defined in this way. Again, because the imports become a static fixture of the WASM file, there is no stack trace which would reveal the code that is actually responsible for generating them.
So this is basically where I'm at at this time. I would welcome some additional context if anyone is willing to share:
I'll try to reach out within the WASM community as well and see if I can get help. Thanks!
Update: I've resolved the following:
You can find the updated code here, and the test project here.
Currently unresolved are the file.try_clone()
calls. I'm unsure how to proceed - is there a polyfill possible here, and what would be the ideal approach?
The std
implementation of try_clone
calls self.inner.duplicate()
. In the case of WASI this is unsupported, and they link to this rationale referencing mmap
. I'm unfamiliar with these things - is try_clone
expected to be implemented by mmap
or have similar behavior?
It looks like even the userspace mmap
support requires conditional compilation of the VM to support it. Any thoughts on how to proceed, or what would make sense for the use cases of Sled?
@spacejam could you provide perspective on how you're using this API, and whether there is a reasonable substitution?
@bitmage thanks for making progress on this!
The fundamental thing happening in the polyfill is to start with a &File, take out the global lock, seek it, and then do a read/write.
For wasi, I see that pread/pwrite are defined symbols but I don't know how hard they are to access. Is it possible to get the underlying file descriptor of an &File on wasi, so that it can be passed to pread/pwrite in libc? If so, we can just use those without any global file lock from a wasi-specific module, similar to what we have for linux and windows.
My use case for this would be to run sled as part of a NodeJS application (which would load sled as a .WASM module)
Thank you @spacejam @bitmage for moving forward on WASM and WASI support.
I have spent some time to make an attempt at it. Here is the result. It seems to work with Rust nightly compiler.
There are still some rought edges:
TMPDIR
environment variable and default to /tmp
. But a better way to go might be to completely disable the temporary database feature from Sled to avoid having to use this hack.wasi_ext
feature that provides a FileExt
trait similar to the UNIX one. It allowed me to reuse the parallel_io_unix
implementation. Moving to Rust stable might be a bit "fun" because getting the file descriptor from a File
object is also unstable in Rust std. Like @bitmage said, reusing naively the parallel_io_polyfill
implementation requires the File::try_clone
operation that is not availlable on WASI.cargo wasi
. There are still some issues because most WASI runtimes mandate to provide a whitelist of files/directories that the code is allowed to open.What is the status of this effort? @Tpt seems to have gone far along, is there a plan to provide WASM support eventually? Most usable embedded DBs lack this at the moment.
Chiming in, would love to know the status of ongoing efforts in this area
Use Case:
Use Sled inside a WASM environment.
Known Issues:
Sled compiles fine for the
wasm32-unknown-unknown
target, but panics at runtime.Currently I'm getting a panic due to time not implemented on wasm32-unknown-unknown.
I assume if this were resolved I would run into errors with disk storage.
Who Benefits From The Change(s)?
I want to use Sled to create a very portable database which will be distributed with and run right next to application code. WASM target is mandatory for my use case. The primary reason I'm using Sled instead of RocksDB is that I figure it would be much more feasible to get it working in WASM. I think this also could become a major selling point of Sled?
Happy to help
I'm interested in working on this. It looks like it may be straightforward, but I have no idea how long the stack of runtime panics will be. Let me know if I'm in over my head. I would appreciate guidance on how you would like the code to be organized, and how to go about the overall process.
cfg
attributes elsewhere in the code looks pretty standard. I would start by refactoring calls toSystemTime::now()
andInstant::now()
into a pair of utility functions. Then placing thecfg
attribute to control implementations for different platforms. I'm looking at js-sys Date.now(). I know the granularity is poor. I think it could be combined with performance.now() to get a monotonic and more accurate time. Actually doing this might involve some tricks.Are you aware of other platform incompatibilities we'll likely run into, besides time and disk storage?
I've read contributing. Any other guidance you have specific to this request?