MarcoPolo – Partially Functional - GoMarcoZola2019-11-06T00:00:00+00:00https://marcopolo.io/tags/go/atom.xmlWasm is the future of serverless. Terrafirma, serverless wasm functions.2019-11-06T00:00:00+00:002019-11-06T00:00:00+00:00https://marcopolo.io/code/terrafirma/<p>When I ran into Fastly's <a href="https://wasm.fastlylabs.com/">Terrarium</a>, the appeal of Webassembly (wasm) finally clicked for me. We could have lightweight sandboxes and bring in my own language and libraries without the overhead of a full OS VM or <a href="https://blog.iron.io/the-overhead-of-docker-run/">Docker</a>. That's great for the serverless provider, but it's also great for the end user. Less overhead means faster startup time and less total cost.</p>
<h2 id="how-much-faster">How much faster?</h2>
<p>On my machine™, a hello world shell script takes 3ms, a docker equivalent takes 700ms, and a wasm equivalent takes 15ms.</p>
<p>Following <a href="https://blog.iron.io/the-overhead-of-docker-run/">this experiment</a> I get these results:</p>
<pre><code>Running: ./hello.sh
avg: 3.516431ms
Running: docker run treeder/hello:sh
avg: 692.306769ms
Running: docker run --rm treeder/hello:sh
avg: 725.912422ms
Running: docker start -a reuse
avg: 655.059021ms
Running: node hello.js
avg: 79.233337ms
Running: wasmer run wasi-hello-world.wasm
avg: 15.155896ms
</code></pre>
<p>When I think about how WASM, Docker, and OS VMs (compute instances) play together, I picture this graph below.</p>
<p><img src="/code/wasm-graph.png" alt="Safety versus overhead – Raw binary is fast unsafe; was is fast and safe; docker is safe." title="Safety vs Overhead" /></p>
<p>The trend is that if you want safety and isolation, you must pay for it with overhead. WASM's exception to that rule is what I think makes it so promising and interesting. Wasm provides the fastest way to run arbitrary user code in a sandboxed environment.</p>
<h2 id="what-is-webassembly">What is Webassembly?</h2>
<p>Webassembly is a spec for a lightweight and sandboxed VM. Webassembly is run by a host, and can't do any side effects, unless it calls a function provided by the host. For example, if your WASM code wanted to make a GET request to a website, it could only do that by asking the host to help. The host exposes these helper function to the WASM guest. In Terrafirma, these are the <code>hostcall_*</code> functions in <a href="https://github.com/MarcoPolo/go-wasm-terrafirma/blob/master/imports.go"><code>imports.go</code></a>. It's called <code>imports.go</code> because it is what your WASM code is importing from the host.</p>
<h2 id="bring-your-own-tools">Bring your own tools</h2>
<p>As long as you can compile everything to a .wasm file, you can use whatever tools and language you want. All I have to do is provide a runtime, and all you have to do is provide a wasm file. However, there is a subtle caveat here. The only way you can run side effects is with the host cooperation. So you (or some library you use) must understand the environment you're running in in order to do anything interesting.</p>
<h2 id="what-about-a-standard-wasm-environment">What about a standard WASM Environment?</h2>
<p>There isn't a mature industry standard for what imports a host should provide to the WASM code running outside the browser. The closest thing we have is <a href="https://wasi.dev/">WASI</a>, which defines a POSIX inspired set of syscalls that a host should implement. It's useful because it allows code would otherwise require a real syscall to work in a WASM environment. For example, In Rust you can build with the <code>--target wasm32-wasi</code> flag and your code will just work in any <a href="https://wasmer.io/">wasi environment</a>.</p>
<h2 id="terrafirma">Terrafirma</h2>
<p>Phew! Finally at TerraFirma. TerraFirma is a WASM runtime environment I wrote to let you run wasm code in the cloud. You upload your wasm file by copying it into a shared <a href="https://keybase.io/docs/kbfs">KBFS folder</a> with the keybase user <a href="https://keybase.io/kbwasm">kbwasm</a>. Then you setup some DNS records to point your domain to TerraFirma's servers. And that's it! You can update the wasm code at any time by overwriting the old .wasm file with the new one.</p>
<h2 id="code-examples">Code Examples</h2>
<ul>
<li><a href="https://github.com/MarcoPolo/terrafirma-hello-world">Hello World</a></li>
<li><a href="https://github.com/MarcoPolo/terrafirma-scraper">Scraper Endpoint</a> – A web scraper that uses Servo – a new browser engine from Mozilla.</li>
</ul>
<h3 id="terrafirma-hello-world-tutorial">Terrafirma – Hello World Tutorial</h3>
<p>This example uses Rust, so if you don't have that setup <a href="https://rustup.rs/">go here first</a>.</p>
<ol>
<li>Point your domain to TerraFirma servers (<code>terrafirma.marcopolo.io</code> or <code>52.53.126.109</code>) with an A record, and set a <code>TXT</code> record to point to your shared folder (e.g. <code>"kbp=/keybase/private/<my_keybase_username>,kbwasm/"</code>)</li>
</ol>
<pre><code>
example.com 300 A terrafirma.marcopolo.io
_keybase_pages.example.com 300 TXT "kbp=/keybase/private/<my_keybase_username>,kbwasm/"
</code></pre>
<ol start="2">
<li>Verify the DNS records are correct</li>
</ol>
<pre><code>
$ dig example.com A
...
;; ANSWER SECTION:
wasm.marcopolo.io. 300 IN A 52.53.126.109
...
</code></pre>
<br/>
<pre><code>
$ dig _keybase_pages.example.com TXT
...
;; ANSWER SECTION:
_keybase_pages.example.com <number> IN TXT "kbp=/keybase/private/<my_keybase_username>,kbpbot/"
...
</code></pre>
<ol start="3">
<li>Clone the Hello World Repo</li>
</ol>
<pre><code>git clone git@github.com:MarcoPolo/terrafirma-hello-world.git
</code></pre>
<ol start="4">
<li>Build it</li>
</ol>
<pre><code>cd terrafirma-hello-world
cargo build --release
</code></pre>
<ol start="5">
<li>Deploy it</li>
</ol>
<pre><code>
cp target/wasm32-unknown-unknown/release/terrafirma_helloworld.wasm /keybase/private/<your_kb_username>,kbwasm/hello.wasm
</code></pre>
<ol start="6">
<li>Test it</li>
</ol>
<pre><code>curl https://example.com/hello.wasm
</code></pre>
Interacting with Go from React Native through JSI2019-06-27T00:00:00+00:002019-06-27T00:00:00+00:00https://marcopolo.io/code/go-rn-jsi/<h1 id="introduction">Introduction</h1>
<p>There are 3 parts that let JS talk to Go:</p>
<ol>
<li>The C++ binding</li>
<li>Installing the binding</li>
<li>Calling Go</li>
</ol>
<p>Not all the code is shown, check out the <a href="https://github.com/MarcoPolo/react-native-hostobject-demo">source code</a> for specifics.</p>
<h3 id="part-1-the-c-binding">Part 1 - The C++ Binding</h3>
<p>The binding is the C++ glue code that will hook up your Go code to the JS runtime. The binding itself is composed of two main parts.</p>
<h4 id="part-1-1-the-c-binding">Part 1.1 - The C++ Binding</h4>
<p>The binding is a c++ class that implements the <code>jsi::HostObject</code> interface. At the very least it's useful for it to have a <code>get</code> method defined. The type of the <code>get</code> method is:</p>
<pre data-lang="c++" class="language-c++ "><code class="language-c++" data-lang="c++">jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
</code></pre>
<p>It returns a <code>jsi::Value</code> (a value that is safe for JS). It's given the JS runtime and the prop string used by JS when it <code>get</code>s the field. e.g. <code>global.nativeTest.foo</code> will call this method with PropNameID === <code>"foo"</code>.</p>
<h4 id="part-1-2-the-c-binding-s-install">Part 1.2 - The C++ Binding's install</h4>
<p>Now that we've defined our HostObject, we need to install it into the runtime. We use a static member function that we'll call later to set this up. It looks like this:</p>
<pre data-lang="c++" class="language-c++ "><code class="language-c++" data-lang="c++">void TestBinding::install(jsi::Runtime &runtime,
std::shared_ptr<TestBinding> testBinding) {
// What is the name that js will use when it reaches for this?
// i.e. `global.nativeTest` in JS
auto testModuleName = "nativeTest";
// Create a JS object version of our binding
auto object = jsi::Object::createFromHostObject(runtime, testBinding);
// set the "nativeTest" propert
runtime.global().setProperty(runtime, testModuleName, std::move(object));
}
</code></pre>
<h3 id="part-2-installing-the-binding-on-android">Part 2. Installing the binding (on Android)</h3>
<p>Since we have a reference to the runtime in Java land, we'll have to create a JNI method to pass the runtime ptr to the native C++ land. We can add this JNI method to our TestBinding file with a guard.</p>
<pre data-lang="c++" class="language-c++ "><code class="language-c++" data-lang="c++">#if ANDROID
extern "C" {
JNIEXPORT void JNICALL Java_com_testmodule_MainActivity_install(
JNIEnv *env, jobject thiz, jlong runtimePtr) {
auto testBinding = std::make_shared<example::TestBinding>();
jsi::Runtime *runtime = (jsi::Runtime *)runtimePtr;
example::TestBinding::install(*runtime, testBinding);
}
}
#endif
</code></pre>
<p>Then on the Java side (after we compile this into a shared library), we register this native function and call it when we're ready.</p>
<pre data-lang="java" class="language-java "><code class="language-java" data-lang="java">// In MainActivity
public class MainActivity extends ReactActivity implements ReactInstanceManager.ReactInstanceEventListener {
static {
// Load our jni
System.loadLibrary("test_module_jni");
}
//... ellided ...
@Override
public void onReactContextInitialized(ReactContext context) {
// Call our native function with the runtime pointer
install(context.getJavaScriptContextHolder().get());
}
// declare our native function
public native void install(long jsContextNativePointer);
}
</code></pre>
<h3 id="part-3-calling-go">Part 3. Calling Go</h3>
<p>Now that our binding is installed in the runtime, we can make it do something useful.</p>
<pre data-lang="c++" class="language-c++ "><code class="language-c++" data-lang="c++">jsi::Value TestBinding::get(jsi::Runtime &runtime,
const jsi::PropNameID &name) {
auto methodName = name.utf8(runtime);
if (methodName == "runTest") {
return jsi::Function::createFromHostFunction(
runtime, name, 0,
[](jsi::Runtime &runtime, const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value { return TestNum(); });
}
return jsi::Value::undefined();
}
</code></pre>
<p>Here we return a <code>jsi::Function</code> when JS calls <code>global.nativeTest.runTest</code>. When JS calls it (as in <code>global.nativeTest.runTest()</code>) we execute the code inside the closure, which just returns <code>TestNum()</code>. TestNum is a Go function that's exported through cgo so that it is available to c/c++. Our Go code looks like this:</p>
<pre data-lang="go" class="language-go "><code class="language-go" data-lang="go">package main
import "C"
// TestNum returns a test number to be used in JSI
//export TestNum
func TestNum() int {
return int(9001)
}
func main() {
}
</code></pre>
<p>cgo builds a header and creates a shared library that is used by our binding.</p>
<h3 id="building">Building</h3>
<ul>
<li>Look at the CMakeLists.txt for specifics on building the C++ code.</li>
<li>Look at from-go/build.sh for specifics on building the go code.</li>
</ul>
<h3 id="a-go-shared-library-for-c-java">A Go Shared Library for C + Java</h3>
<p>It's possible to build the Go code as a shared library for both C and Java, but you'll have to define your own JNI methods. It would be nice if gomobile bind also generated C headers for android, but it doesn't seem possible right now. Instead you'll have to run <code>go build -buildmode=c-shared</code> directly and define your jni methods yourself. Take a look at <code>from-go/build.sh</code> and testnum.go for specifics.</p>
<h2 id="further-reading">Further Reading</h2>
<p><a href="https://medium.com/@christian.falch/https-medium-com-christian-falch-react-native-jsi-challenge-1201a69c8fbf">JSI Challenge #1</a></p>
<p><a href="https://medium.com/@christian.falch/react-native-jsi-challenge-2-56fc4dd91613">JSI Challenge #2</a></p>
<p><a href="http://blog.nparashuram.com/2019/01/react-natives-new-architecture-glossary.html">RN Glossary of Terms</a></p>
<p><a href="https://blog.dogan.io/2015/08/15/java-jni-jnr-go/">GO JNI</a></p>
<p><a href="https://rakyll.org/cross-compilation/">GO Cross Compilation</a></p>
Introducing Servant, a Clojurescript library for web workers2013-10-01T00:00:00+00:002013-10-01T00:00:00+00:00https://marcopolo.io/code/servant-cljs/<h1 id="concurrent-programming">Concurrent Programming</h1>
<p>Javascript by default is single threaded, but web workers introduce
OS level threads. Concurrent programming is hard enough (in imperative
languages), so the webworker designers decided to circumvent a bunch of
concurrency problems by forbidding any shared data between threads. There are
better ways of doing this (read <em>immutability</em>), but we work with what we got.</p>
<p><br></br></p>
<h1 id="problems-with-web-workers">Problems with web workers</h1>
<p>I've done a couple projects with web workers. The biggest project being
<a href="http://cryptic.io">Cryptic.io</a>, which uses webworkers to efficiently
encrypt/decrypt large (GBs) files, and parallel {down,up}load file chunks. Here
are problems I've stumbled across:</p>
<ul>
<li>Everything about the web worker needs to be asynchronous, meaning callback hell</li>
<li>You need to think in a separate context for the web worker, you can't call any functions defined with the rest of your code.</li>
<li>Distributing workload effectively.</li>
<li>The problems only gets worse the more web workers you bring in.</li>
</ul>
<br />
<h1 id="enter-servant">Enter Servant</h1>
<p><a href="https://github.com/marcopolo/servant">Servant</a> is a super small (literally ~100 lines) library that solves all the
problems above, allowing you to write clean, multithreaded, ClojureScript. Even
though it's small, it does a lot.</p>
<ul>
<li>It allows you to define servant functions alongside the rest of your code, even using functions already defined in your
namespace.</li>
<li>It automatically balances work across webworkers.</li>
<li>It provides simple ways to do a normal (copy of arguments) or efficient (arraybuffer transfer) call
to webworkers, easily.</li>
</ul>
<h1 id="sharing-functions-and-predefined-variables">Sharing functions, and predefined variables</h1>
<p>This was the trickiest part of the library. I wanted the ability to define
useful functions, and use them in the webworker without having to copy it over
to a separate worker.js file. I solved it by using the same exact file for both
the main page (browser context) and the web worker. That, however, came with one
problem; you have to explicitly declare code that should run on the webworker
and code that runs in the browser. Like so:</p>
<pre data-lang="clojure" class="language-clojure "><code class="language-clojure" data-lang="clojure">(ns servant.demo
(:require [servant.core :as servant]
[servant.worker :as worker]))
(defn window-load [] (.log js/console "this runs in the browser"))
(if (servant/webworker?)
(worker/bootstrap) ;;Sets the worker up to receive messages
(set! (.-onload js/window) window-load))
</code></pre>
<p>As part of that caveat, the webworker can only see code that it can get to.
Anything defined in window-load would not be visible to the webworker. Now let's
take a look at how we can define a servant function, using the <code>defservantfn</code>
macro.</p>
<p>We need to use a special function, <code>defservantfn</code> to define functions that will
serve as our "access points" to the web worker.</p>
<pre data-lang="clojure" class="language-clojure "><code class="language-clojure" data-lang="clojure">(ns servant.demo
(:require-macros [servant.macros :refer [defservantfn]]))
(defn make-it-funny [not-funny]
(str "Hahahah:" not-funny))
(defservantfn servant-with-humor [your-joke]
(make-it-funny your-joke))
</code></pre>
<p>The <code>defservantfn</code> macro simply calls a defn with the
same arguments, and registers that function with a hashmap atom for the
webworker. The key is the hash of the function and the value is the function
itself. The webworker needs to be able to know what function the browser is
referring in a message, so I use the function's hash as a token that
the browser context and webworker can both agree on. The function's
<code>.toString()</code> value could have worked just as well.</p>
<p>I should also mention, for efficiency reasons, Servant keeps a pool of N
webworkers (you specify N) alive (until you explicitly kill them) so you only
pay for the webworkers once. You control when the webworkers are created with
<code>servant/spawn-servants</code>.</p>
<h1 id="workload-balancing">Workload Balancing</h1>
<p>Core.async is simply amazing, it took this tricky problem and made it trivial. The solution is 4 lines.
The solution for Servant is:</p>
<ul>
<li>spawn N number of workers and place them in a buffered (of size N) channel.</li>
<li>Take workers from the channel as you use them.</li>
<li>Put them back when you get your result.</li>
</ul>
<p>This is so beautifully simple. I just write the behavior I want, and core.async
handles the messy state. If all the webworkers are busy the code will "block"
until a webworker is free. What this means for you as a user, is you don't have
to think about which worker is available to run your code.</p>
<h1 id="configurable-message-types">Configurable message types</h1>
<p>Now the whole point of using webworkers is to be as fast as possible. Sometimes
you can't even afford copying data to the webworker (especially if the data is
big, like at <a href="http://cryptic.io">Cryptic.io</a>). Servant provides a way to access
webworkers' nifty <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)">arraybuffer transfer context ability</a>.
Take for example:</p>
<pre data-lang="clojure" class="language-clojure "><code class="language-clojure" data-lang="clojure">(defservantfn get-first-4bytes-as-str [arraybuffer]
(let [d (js/DataView. arraybuffer)]
(.toString (.getUint32 d 0) 16)))
</code></pre>
<p>That function expects an arraybuffer and returns a string. If we wanted to be
efficient about it (and didn't care about getting rid of the arraybuffer) we can
make the call using the <code>servant/array-buffer-message-standard-reply</code> fn instead
of the <code>servant/standard-message</code>. So the efficient result would be:</p>
<pre data-lang="clojure" class="language-clojure "><code class="language-clojure" data-lang="clojure">(def arraybuffer (js/ArrayBuffer. 10))
(def d (js/DataView. arraybuffer))
(.setUint32 d 0 0xdeadbeef)
(def result-channel
(servant/servant-thread
servant-channel
servant/array-buffer-message-standard-reply
get-first-4bytes-as-str arraybuffer [arraybuffer]))
</code></pre>
<p>The arguments to servant-thread are:</p>
<ul>
<li><code>servant-channel</code> - channel that contains the available workers</li>
<li><code>servant/array-buffer-message-standard-reply</code> - A function that defines how the <code>.postMessage</code> function will be called (a.k.a mesage-fn)</li>
<li><code>get-first-4bytes-as-str</code> - The servant function we defined earlier</li>
<li><code>arraybuffer</code> - our argument to the function</li>
<li><code>[arraybuffer]</code> - a vector of arraybuffers that are going to be transferred</li>
</ul>
<p>The message-fn can be anything, but I think servant has you covered with:</p>
<ul>
<li><code>standard-message</code> : Copies all the data both ways</li>
<li><code>array-buffer-message</code> : <em>Can</em> transfer the context both ways</li>
<li><code>array-buffer-message-standard-reply</code> : <em>Can</em> transfer the context when making the call, <em>won't</em> transfer the context coming back</li>
</ul>
<p>There is a reason why array-buffer-message isn't just the standard. You need to
explicitly tell the postMessage call that you want to transfer arraybuffers. So
to transfer context you need an additional argument, an array of arraybuffers.
You also need to make sure the defservantfn returns a vector of results and an
array of arraybuffers [result [arraybuffer1]] if you want to transfer the
arraybuffer from the worker to the browser context. I figured if you wanted
that you could use it and deal with the extra argument, if you didn't you could
write your functions how you normally would.</p>
<h1 id="examples">Examples</h1>
<p>I wrote two examples using the servant library:</p>
<ul>
<li>The first is a <a href="https://github.com/MarcoPolo/servant-demo">simple demo</a> showing several use cases.</li>
<li>The next is more featured demo that can encrypt/decrypt large files efficiently using webworkers.</li>
</ul>
<h1 id="last-thoughts">Last thoughts</h1>
<p>I used to curse the name webworkers. They brought gifts of speed at the cost of
complexity. Servant is different, it doesn't sacrifice simplicity or
efficiency. I'm pretty excited at the ease of using webworkers with servant, and
I hope you have fun making an amazing, multithreaded Clojurescript application!</p>
<br />