{"id":655,"date":"2019-11-16T00:00:00","date_gmt":"2019-11-16T00:00:00","guid":{"rendered":"https:\/\/en.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/"},"modified":"2025-03-04T05:43:57","modified_gmt":"2025-03-04T13:43:57","slug":"how-we-compiled-a-golang-database-in-the-browser-using-webassembly","status":"publish","type":"post","link":"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/","title":{"rendered":"How We Compiled a Golang Database in the Browser Using WebAssembly"},"content":{"rendered":"\n<p>As Queeny Jin mentioned in her article, <a href=\"https:\/\/pingcap.com\/blog\/tidb-in-the-browser-running-a-golang-database-on-webassembly\/\">TiDB in the Browser: Running a Golang Database on WebAssembly<\/a>, we compiled TiDB into an in-browser database using WebAssembly (Wasm). We&#8217;re very proud of this pilot project because it opens a door to an entirely new world for both Golang and Wasm:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It is probably the first Golang database that&#8217;s been compiled to Wasm. As &#8220;<a href=\"https:\/\/golangweekly.com\/issues\/287\">Golang weekly (issue 287)<\/a>&#8221; put it, &#8220;The author wonders if a database like TiDB written in Go can run in a web browser, what about other complex Go apps? Go&#8217;s WebAssembly future is looking quite positive.&#8221;<\/li>\n\n\n\n<li>Besides <a href=\"https:\/\/github.com\/kripken\/sql.js\/\">SQLite<\/a>, Wasm has one more database example that can run inside the browser. As <a href=\"https:\/\/dbweekly.com\/issues\/279\">Database Weekly (issue 279)<\/a> put it, &#8220;If a database like TiDB written in Go can run in the browser, what about other complex Go apps or other database systems?&#8221;<\/li>\n<\/ul>\n\n\n\n<p>What&#8217;s most exciting is that beginning database users now have an easy way to learn to write SQL statements or test new databases. They don&#8217;t have to download an entire database and go through the complex setup and configuration process to be able to write SQL. They can simply open their browser, wait a few seconds for the TiDB database to load, and then start to write SQL statements.<\/p>\n\n\n\n<p>The time to experiment with SQL without pain has come. In fact, the community has started to hack on this project, and they&#8217;ve built a markdown-it plugin to run a TiDB Wasm instance on markdown. Now you can have an interactive playground to learn SQL in the browser: <a href=\"https:\/\/github.com\/imiskolee\/tidb-wasm-markdown\">https:\/\/github.com\/imiskolee\/tidb-wasm-markdown<\/a><\/p>\n\n\n\n<p>The source code for this project is in <a href=\"https:\/\/github.com\/pingcap\/tidb\/pull\/13069\">PR: #13069 &#8211; support compiling tidb to wasm<\/a>. You are welcome to join us!<\/p>\n\n\n\n<p>The rest of this blog will dive deep into how and why we built an in-browser database. By the end of the article, you&#8217;ll know how to reproduce it yourself, create your own projects, and get inspired.<\/p>\n\n\n\n<p><strong>TL;DR<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Want_to_make_your_own_Golang_apps_run_in_a_browser_Heres_what_we_learned_%E2%80%A6\"><\/span><em>Want to make your own Golang apps run in a browser? Here&#8217;s what we learned &#8230;<\/em><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p><em>We had a lot of fun\u2014and learned a lot\u2014from our little adventure at TiDB Hackathon 2019. If you want to make your own Golang applications run in a browser, here are some suggestions:<\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>As <a href=\"https:\/\/wasmweekly.news\/issue-100\/\">WebAssembly Weekly &#8211; Issue #100<\/a> mentioned, &#8220;WebAssembly support for Go applications is very much in its infancy.&#8221; For example, Golang hasn&#8217;t fully supported WASI, and goleveldb doesn&#8217;t support Wasm\/js. Be discreet before you go too deep, and clearly understand the use cases for your application.<\/em><\/li>\n\n\n\n<li><em>Make sure your application doesn&#8217;t have third-party platform-specific dependencies that can&#8217;t be compiled to Wasm.<\/em><\/li>\n\n\n\n<li><em>Browsers don&#8217;t allow port listening and file operations, so you&#8217;ll have to work around those limitations.<\/em><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Why_an_in-browser_database\"><\/span>Why an in-browser database?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>The idea came to us when we were planning our TiDB Hackathon 2019. The goal of participating in the Hackathon was very simple: hack something cool together! (So cool that we name ourselves: Ti-Cool.) What could be cooler than an in-browser database?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"How_we_made_it_possible\"><\/span>How we made it possible?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>It&#8217;s a hackathon, so we hack, of course. =)<\/p>\n\n\n\n<p>Let&#8217;s take a look at our goal again: building an in-browser TiDB database. Breaking it down, we got a few questions to answer:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Can TiDB be compiled to an in-browser application? If yes, how?<\/li>\n\n\n\n<li>How can users input the SQL statements and get their results just from the browser?<\/li>\n<\/ol>\n\n\n\n<p>Let&#8217;s find out.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Question #1: Can TiDB be compiled to an in-browser application? If yes, how?<\/h3>\n\n\n\n<p><strong>What we knew:<\/strong> TiDB is written in Golang, and Go 1.11 added a port to WebAssembly.<\/p>\n\n\n\n<p><strong>What we didn&#8217;t know:<\/strong> Does TiDB include any platform-specific libraries that can&#8217;t be compiled to Wasm?<\/p>\n\n\n\n<p>We started our adventure of compiling TiDB to Wasm by following <a href=\"https:\/\/github.com\/golang\/go\/wiki\/WebAssembly#getting-started\">getting started with WebAssembly<\/a>. Here was our first setback:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"201\" src=\"https:\/\/static.pingcap.com\/files\/2019\/11\/04054341\/getting-started-with-webassembly-1024x201.png\" alt=\"getting-started-with-webassembly\" class=\"wp-image-25456\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/04054341\/getting-started-with-webassembly-1024x201.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/04054341\/getting-started-with-webassembly-300x59.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/04054341\/getting-started-with-webassembly-768x150.png 768w, https:\/\/static.pingcap.com\/files\/2019\/11\/04054341\/getting-started-with-webassembly.png 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Going through the goleveldb code and the storage package led to the heartbreaking fact that even though it had implementations for most platforms, it didn&#8217;t have one for Wasm:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"292\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/goleveldb-code-and-the-storage-package.png\" alt=\"The goleveldb code and the storage package\" class=\"wp-image-656\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/goleveldb-code-and-the-storage-package.png 1200w, https:\/\/static.pingcap.com\/files\/2019\/11\/goleveldb-code-and-the-storage-package-300x73.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/goleveldb-code-and-the-storage-package-1024x249.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/goleveldb-code-and-the-storage-package-768x187.png 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>That&#8217;s why we had the <code>undefined<\/code> error messages about <code>newFileLock<\/code>, <code>rename<\/code>, <code>syncDir<\/code>, etc.<\/p>\n\n\n\n<p>As shown in the code below, we decided to add a <code>file_storage_js.go<\/code> file and leave those functions unimplemented:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package storage\n\nimport (\n  \"os\"\n  \"syscall\"\n)\n\nfunc newFileLock(path string, readOnly bool) (fl fileLock, err error) {\n  return nil, syscall.ENOTSUP\n}\n\nfunc setFileLock(f *os.File, readOnly, lock bool) error {\n  return syscall.ENOTSUP\n}\n\nfunc rename(oldpath, newpath string) error {\n  return syscall.ENOTSUP\n}\n\nfunc isErrInvalid(err error) bool {\n  return false\n}\n\nfunc syncDir(name string) error {\n  return syscall.ENOTSUP\n}\n<\/code><\/pre>\n\n\n\n<p>We compiled the code again and got this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"182\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/missing-function-body.png\" alt=\"Missing function body\" class=\"wp-image-657\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/missing-function-body.png 1200w, https:\/\/static.pingcap.com\/files\/2019\/11\/missing-function-body-300x46.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/missing-function-body-1024x155.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/missing-function-body-768x116.png 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Um&#8230; What does <code>missing function body<\/code> mean?<\/p>\n\n\n\n<p>Diving deep into the code, we found that there were function declarations in the <code>arith_decl.go<\/code> file but the functions weren&#8217;t implemented in the system-specific files.<\/p>\n\n\n\n<p>This problem was similar to the one we had before, and we could solve it the same way, except there was a problem: this code belonged to a third-party library and was not in our control. Besides, TiDB did not depend on the library directly\u2014it depended on the <code>mathutil<\/code> library which depended on the <code>bigfft<\/code> library, and neither of these libraries was in our control. Two plans came to our minds:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Plan 1. File pull requests to the <code>mathutil<\/code> and <code>bigfft<\/code> libraries so that they can support Wasm.<\/li>\n\n\n\n<li>Plan 2. Clone the <code>mathutil<\/code> and <code>bigfft<\/code> libraries, add the functions and leave them unimplemented, and change the TiDB dependency to our cloned libraries.<\/li>\n<\/ul>\n\n\n\n<p>Plan 1 was quickly ruled out because we couldn&#8217;t do it in time for the hackathon, not to mention we had no idea whether the maintainers of these two projects would approve our changes and merge them.<\/p>\n\n\n\n<p>Plan 2 had its own problem because it meant we would probably detach ourselves from the upstreams.<\/p>\n\n\n\n<p>Was there a third way? Could we make a special case where TiDB didn&#8217;t depend on these two libraries when we compile the Wasm binary format, but when it came to the binary format for other platforms like Linux, it&#8217;s business as usual?<\/p>\n\n\n\n<p>With this goal in mind, we did some homework. We found that even though <code>mathutil<\/code> was called many times, only the following functions were used: <code>MinUint64<\/code>, <code>MaxUint64<\/code>, <code>MinInt32<\/code>, <code>MaxInt32<\/code>, etc. So we had an idea:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create the <code>mathutil<\/code> directory, then the <code>mathutil_linux.go<\/code> and <code>mathutil_js.go<\/code> files.<\/li>\n\n\n\n<li>In the <code>mathutil_linux.go<\/code> file, re-export to the third party libraries.<\/li>\n\n\n\n<li>In the <code>mathutil_js.go<\/code> file, create these functions and leave them unimplemented, with no dependency on any third-party libraries.<\/li>\n\n\n\n<li>Switch all the dependency on third-party libraries to the <code>mathutil<\/code> directory so that only the <code>mathutil<\/code> directory included the third-party package that didn&#8217;t support Wasm.<\/li>\n<\/ol>\n\n\n\n<p>In this way, the <code>mathutil<\/code> directory provided all the functions in the original <code>mathutil<\/code> package. In addition, when compiling TiDB for other platforms (Linux), it could still go to the <code>mathutil_linux.go<\/code> file which led to the third-party libraries; when compiling TiDB for Wasm, it could go to the <code>mathutil_js.go<\/code> file.<\/p>\n\n\n\n<p>Compiling again:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"178\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application.png\" alt=\"TiDB could be compiled to an in-browser application\" class=\"wp-image-658\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application.png 1600w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application-300x33.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application-1024x114.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application-768x85.png 768w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application-1536x171.png 1536w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-could-be-compiled-to-an-in-browser-application-1440x160.png 1440w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Yay! We made it! The <code>main.wasm<\/code> is the compiled TiDB Wasm file we wanted, and it proves that TiDB could be compiled to an in-browser application.<\/p>\n\n\n\n<p>Ok. Now we have a compiled TiDB Wasm. Let&#8217;s run it, and see what comes out!<\/p>\n\n\n\n<p>Theoretically, because we were using <code>os.Stdin<\/code> as the input and the <code>os.Stdout<\/code> as the output, and we hadn&#8217;t done anything with DOM yet, the output should be blank. However, TiDB outputs its log to <code>os.Stdout<\/code>, we expect a log message that TiDB started successfully. Unfortunately, this was what we got:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"490\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/runtime-did-not-implement-os-stat.png\" alt=\"Runtime didn't implement os.stat\" class=\"wp-image-659\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat.png 1600w, https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat-300x92.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat-1024x314.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat-768x235.png 768w, https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat-1536x470.png 1536w, https:\/\/static.pingcap.com\/files\/2019\/11\/runtime-did-not-implement-os-stat-1440x441.png 1440w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>What we could derive from the error message was that the runtime didn&#8217;t implement <code>os.stat<\/code>. We determined that this was because Golang hasn&#8217;t fully supported WASI yet. As shown in the following code, it merely mocked an <code>fs<\/code> in <code>wasm_exec.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>global.fs = {\n        writeSync(fd, buf) {\n                ...\n        },\n        write(fd, buf, offset, length, position, callback) {\n                ...\n        },\n        open(path, flags, mode, callback) {\n                ...\n        },\n        ...\n}\n<\/code><\/pre>\n\n\n\n<p>The mocked <code>fs<\/code> didn&#8217;t implement the callbacks such as <code>stat<\/code>, <code>lstat<\/code>, <code>unlink<\/code>, <code>mkdir<\/code>. So we mocked these functions in the global <code>fs<\/code> object before the Wasm was started:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function unimplemented(callback) {\n    const err = new Error(\"not implemented\");\n    err.code = \"ENOSYS\";\n    callback(err);\n}\nfunction unimplemented1(_1, callback) { unimplemented(callback); }\nfunction unimplemented2(_1, _2, callback) { unimplemented(callback); }\n\nfs.stat = unimplemented1;\nfs.lstat = unimplemented1;\nfs.unlink = unimplemented1;\nfs.rmdir = unimplemented1;\nfs.mkdir = unimplemented2;\ngo.run(result.instance);\n<\/code><\/pre>\n\n\n\n<div class=\"trackable-btns\"><a href=\"\/download\"><button>Download TiDB<\/button><\/a><br><a href=\"https:\/\/share.hsforms.com\/1e2W03wLJQQKPd1d9rCbj_Q2npzm\"><button>Subscribe to Blog<\/button><\/a><\/div>\n\n\n\n<p>When we refreshed the page, the expected TiDB log was finally displayed:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"712\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/tidb-log-was-finally-displayed.png\" alt=\"TiDB log was finally displayed\" class=\"wp-image-660\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed.png 1600w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed-300x134.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed-1024x456.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed-768x342.png 768w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed-1536x684.png 1536w, https:\/\/static.pingcap.com\/files\/2019\/11\/tidb-log-was-finally-displayed-1440x641.png 1440w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Yay, we made it again!<\/p>\n\n\n\n<p>At this point, we had cleared all the technical blockers. Now it was time to get inputs and outputs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Question # 2: How can users input the SQL statements and get their results from the browser?<\/h3>\n\n\n\n<p>We all know that browsers cannot let the web applications within them do dangerous things such as port listening and file operations. However, TiDB lets users start a client and connect to TiDB using MySQL statements (<a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/mysql-compatibility\">mostly<\/a>), which means users need to listen on a certain port. We want our users to have a built-in TiDB client in the Wasm file together with the TiDB sever: When the browser loads the Wasm file, the client is launched immediately to allow users to input SQL statements and output the SQL results.<\/p>\n\n\n\n<p>Here is what we did:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Reuse what&#8217;s already in the TiDB test code. We looked through the test code in TiDB and found the following:<br><pre><code class=\"language-go\">result = tk.MustQuery(\"select count(*) from t group by d order by c\")<br>result.Check(testkit.Rows(\"3\", \"2\", \"2\"))<br><\/code><\/pre><br><\/li>\n\n\n\n<li>Locate the <code>tk<\/code> function:<br><pre><code class=\"language-go\">\/\/ Exec executes a sql statement.<br>func (tk *TestKit) Exec(sql string, args ...interface{}) (sqlexec.RecordSet, error) {<br>    var err error<br>    if tk.Se == nil {<br>        tk.Se, err = session.CreateSession4Test(tk.store)<br>        tk.c.Assert(err, check.IsNil)<br>        id := atomic.AddUint64(&amp;connectionID, 1)<br>        tk.Se.SetConnectionID(id)<br>    }<br>    ctx := context.Background()<br>    if len(args) == 0 {<br>        var rss []sqlexec.RecordSet<br>        rss, err = tk.Se.Execute(ctx, sql)<br>        if err == nil &amp;&amp; len(rss) > 0 {<br>            return rss[0], nil<br>        }<br>        return nil, errors.Trace(err)<br>    }<br>    stmtID, _, _, err := tk.Se.PrepareStmt(sql)<br>    if err != nil {<br>        return nil, errors.Trace(err)<br>    }<br>    params := make([]types.Datum, len(args))<br>    for i := 0; i &lt; len(params); i++ {<br>        params[i] = types.NewDatum(args[i])<br>    }<br>    rs, err := tk.Se.ExecutePreparedStmt(ctx, stmtID, params)<br>    if err != nil {<br>        return nil, errors.Trace(err)<br>    }<br>    err = tk.Se.DropPreparedStmt(stmtID)<br>    if err != nil {<br>        return nil, errors.Trace(err)<br>    }<br>    return rs, nil<br>}<br><\/code><\/pre><br><\/li>\n\n\n\n<li>Write a read-eval-print-loop (REPL) to take inputs, execute them using the above <code>Exec<\/code> function, return the result to standard output, and loop the entire process.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">What about the terminal?<\/h3>\n\n\n\n<p>We now had an <code>Exec<\/code> function that could take SQL statements, output the result, and run in the browser. We also needed an SQL client to interact with the function. We considered the following options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use Golang to manipulate dom to implement this client.<\/li>\n\n\n\n<li>Use Golang to expose the <code>Exec<\/code> function to the global, and find an existing SQL client\/terminal in JavaScript(JS) to interact with the <code>Exec<\/code> function.<\/li>\n<\/ul>\n\n\n\n<p>Our current team members have limited front-end knowledge, so we chose the second approach. We found the <code>jquery.console.js<\/code> library, which required only a callback from the SQL, and our <code>Exec<\/code> fit right in.<\/p>\n\n\n\n<p>Here are the specific steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Exposed the <code>Exec<\/code> function globally to be called back by JS:<br><pre><code class=\"language-go\">js.Global().Set(\"executeSQL\", js.FuncOf(func(this js.Value, args []js.Value) interface{} {<br>    go func() {<br>    \/\/ Simplified code<br>        sql := args[0].String()<br>        args[1].Invoke(k.Exec(sql))<br>    }()<br>    return nil<br>}))<br><\/code><\/pre><br><p>Result: We can run SQL from the browser console:<\/p><br><figure><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"542\" class=\"wp-image-661\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/run-sql-from-the-browser-console.png\" alt=\"Run SQL from the browser console\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console.png 1600w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console-300x102.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console-1024x347.png 1024w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console-768x260.png 768w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console-1536x520.png 1536w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-sql-from-the-browser-console-1440x488.png 1440w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><\/figure><br><\/li>\n\n\n\n<li>Built an SQL client using <code>jquery.console.js<\/code> and passed the <code>executeSQL<\/code> as callbacks:Result: Now you can run the Golang database the SQL directly in your browser!<figure><img loading=\"lazy\" decoding=\"async\" width=\"700\" height=\"304\" class=\"wp-image-662\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/run-the-golang-database-in-the-browser.png\" alt=\"Run the Golang database in the browser\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/run-the-golang-database-in-the-browser.png 700w, https:\/\/static.pingcap.com\/files\/2019\/11\/run-the-golang-database-in-the-browser-300x130.png 300w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/figure><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">One more thing: taking in local files<\/h3>\n\n\n\n<p>Our users now have an in-browser database where they can write SQL directly. However, there&#8217;s a small problem: they can only write one SQL statement at one time.<\/p>\n\n\n\n<p>Imagine if a user wanted to test the compatibility between TiDB and MySQL. It would be a nightmare to run the statements one by one.<\/p>\n\n\n\n<p>However, in TiDB we have features such as <code>load stats<\/code> and <code>load data<\/code> to read and load content from files into the databases, see <a href=\"https:\/\/docs.pingcap.com\/tidb\/stable\/sql-statement-load-data#load-data\">for more information<\/a>. But as we mentioned earlier, this was not possible for a browser.<\/p>\n\n\n\n<p>To resolve this issue, we did the following:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Opened the browser&#8217;s <code>upload<\/code> window so users could choose a file and upload it to TiDB:<br><pre><code class=\"language-go\">js.Global().Get(\"upload\").Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} {<br>    go func() {<br>        fileContent := args[0].String()<br>        _, e := doSomething(fileContent)<br>        c &lt;- e<br>    }()<br>    return nil<br>}), js.FuncOf(func(this js.Value, args []js.Value) interface{} {<br>    go func() {<br>        c &lt;- errors.New(args[0].String())<br>    }()<br>    return nil<br>}))<br><\/code><\/pre><br><\/li>\n\n\n\n<li>Added a <code>source<\/code> command. Users can run the <code>source<\/code> command to upload a SQL file, and the TiDB Wasm executes the SQL statements in the file.For example, users could enter the <code>source<\/code> command. The browser displays a window for you to load an SQL file.<figure><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"477\" class=\"wp-image-663\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/load-an-sql-file.png\" alt=\"Load an SQL file\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/load-an-sql-file.png 1000w, https:\/\/static.pingcap.com\/files\/2019\/11\/load-an-sql-file-300x143.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/load-an-sql-file-768x366.png 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure><br><p>If you load the <code>test.sql<\/code> file:<\/p><br><pre><code class=\"language-sql\">CREATE DATABASE IF NOT EXISTS samp_db;<br><br>USE samp_db;<br><br>CREATE TABLE IF NOT EXISTS person (<br>    number INT(11),<br>    name VARCHAR(255),<br>    birthday DATE<br>);<br><br>CREATE INDEX person_num ON person (number);<br><br>INSERT INTO person VALUES(\"1\",\"tom\",\"20170912\");<br><br>UPDATE person SET birthday='20171010' WHERE name='tom';<br><\/code><\/pre><br><p>And then show the database, you see the following results:<\/p><br><figure><img loading=\"lazy\" decoding=\"async\" width=\"812\" height=\"370\" class=\"wp-image-664\" src=\"https:\/\/en.pingcap.com\/wp-content\/uploads\/2019\/11\/the-result.png\" alt=\"The result\" srcset=\"https:\/\/static.pingcap.com\/files\/2019\/11\/the-result.png 812w, https:\/\/static.pingcap.com\/files\/2019\/11\/the-result-300x137.png 300w, https:\/\/static.pingcap.com\/files\/2019\/11\/the-result-768x350.png 768w\" sizes=\"auto, (max-width: 812px) 100vw, 812px\" \/><\/figure><br><\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Whats_next\"><\/span>What&#8217;s next<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Because of the limited time in Hackathon, TiDB Wasm could only serve as a pilot project. If you use TiDB Wasm, keep in mind these <a href=\"https:\/\/pingcap.com\/blog\/tidb-in-the-browser-running-a-golang-database-on-webassembly\/#what-are-the-limitations\">caveats<\/a>. We also have a lot more work to do, including the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Persist data using IndexedDB, and implement a storage interface for IndexDB.<\/li>\n\n\n\n<li>Apply P2P technologies such as WebRTC to provide service to other browsers. We believe there will be more and more applications migrating to Wasm and many of them will need databases like TiDB Wasm.<\/li>\n\n\n\n<li>Make the Wasm files smaller. The current files are almost 80 MB and occupy too much memory. This is not friendly to browser. (Thanks a lot to <a href=\"https:\/\/github.com\/syrusakbary\">Syrus Akbary<\/a> for compiling TiDB to WASI; it&#8217;s a pity that it&#8217;s too large to run on any runtime.)<\/li>\n\n\n\n<li>Publish TiDB to WAPM and run it in <a href=\"https:\/\/webassembly.sh\/\">WebAssembly shell<\/a>.<\/li>\n\n\n\n<li>And more&#8230;<\/li>\n<\/ul>\n\n\n\n<p>You are welcome to join us and build this project together at https:\/\/github.com\/pingcap\/tidb\/projects\/27 or contact us at <a href=\"mailto:info@pingcap.com\">info@pingcap.com<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We compiled a Golang database (TiDB) into an in-browser database using WebAssembly (Wasm). This post introduces why and how we built an in-browser database.<\/p>","protected":false},"author":70,"featured_media":666,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ub_ctt_via":"","footnotes":""},"categories":[6],"tags":[53,44],"class_list":["post-655","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-go","tag-hackathon"],"acf":[],"featured_image_src":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","author_info":{"display_name":"Joshua Zhou","author_link":"https:\/\/www.pingcap.com\/ko\/blog\/author\/joshua-zhou\/"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.9 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How We Compiled a Golang Database in the Browser<\/title>\n<meta name=\"description\" content=\"In this post, we will dive deep into how and why we built an in-browser database, and you&#039;ll know how to reproduce it yourself.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\" \/>\n<meta property=\"og:locale\" content=\"ko_KR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How We Compiled a Golang Database in the Browser\" \/>\n<meta property=\"og:description\" content=\"In this post, we will dive deep into how and why we built an in-browser database, and you&#039;ll know how to reproduce it yourself.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\" \/>\n<meta property=\"og:site_name\" content=\"TiDB\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/facebook.com\/pingcap2015\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-16T00:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-04T13:43:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"667\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Joshua Zhou\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:site\" content=\"@PingCAP\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Joshua Zhou\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12\ubd84\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\"},\"author\":{\"name\":\"Joshua Zhou\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/c4880334852b40d445e34c2c608a4cb0\"},\"headline\":\"How We Compiled a Golang Database in the Browser Using WebAssembly\",\"datePublished\":\"2019-11-16T00:00:00+00:00\",\"dateModified\":\"2025-03-04T13:43:57+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\"},\"wordCount\":1933,\"publisher\":{\"@id\":\"https:\/\/www.pingcap.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\",\"keywords\":[\"Go\",\"Hackathon\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"ko-KR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\",\"url\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\",\"name\":\"How We Compiled a Golang Database in the Browser\",\"isPartOf\":{\"@id\":\"https:\/\/www.pingcap.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\",\"datePublished\":\"2019-11-16T00:00:00+00:00\",\"dateModified\":\"2025-03-04T13:43:57+00:00\",\"description\":\"In this post, we will dive deep into how and why we built an in-browser database, and you'll know how to reproduce it yourself.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#breadcrumb\"},\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage\",\"url\":\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\",\"width\":2000,\"height\":667},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.pingcap.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How We Compiled a Golang Database in the Browser Using WebAssembly\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.pingcap.com\/#website\",\"url\":\"https:\/\/www.pingcap.com\/\",\"name\":\"TiDB\",\"description\":\"TiDB | SQL at Scale\",\"publisher\":{\"@id\":\"https:\/\/www.pingcap.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.pingcap.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ko-KR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.pingcap.com\/#organization\",\"name\":\"PingCAP\",\"url\":\"https:\/\/www.pingcap.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png\",\"width\":811,\"height\":232,\"caption\":\"PingCAP\"},\"image\":{\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/facebook.com\/pingcap2015\",\"https:\/\/x.com\/PingCAP\",\"https:\/\/linkedin.com\/company\/pingcap\",\"https:\/\/youtube.com\/channel\/UCuq4puT32DzHKT5rU1IZpIA\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/c4880334852b40d445e34c2c608a4cb0\",\"name\":\"Joshua Zhou\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.pingcap.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg\",\"contentUrl\":\"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg\",\"caption\":\"Joshua Zhou\"},\"url\":\"https:\/\/www.pingcap.com\/ko\/blog\/author\/joshua-zhou\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How We Compiled a Golang Database in the Browser","description":"In this post, we will dive deep into how and why we built an in-browser database, and you'll know how to reproduce it yourself.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/","og_locale":"ko_KR","og_type":"article","og_title":"How We Compiled a Golang Database in the Browser","og_description":"In this post, we will dive deep into how and why we built an in-browser database, and you'll know how to reproduce it yourself.","og_url":"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/","og_site_name":"TiDB","article_publisher":"https:\/\/facebook.com\/pingcap2015","article_published_time":"2019-11-16T00:00:00+00:00","article_modified_time":"2025-03-04T13:43:57+00:00","og_image":[{"width":2000,"height":667,"url":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","type":"image\/jpeg"}],"author":"Joshua Zhou","twitter_card":"summary_large_image","twitter_creator":"@PingCAP","twitter_site":"@PingCAP","twitter_misc":{"Written by":"Joshua Zhou","Est. reading time":"12\ubd84"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#article","isPartOf":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/"},"author":{"name":"Joshua Zhou","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/c4880334852b40d445e34c2c608a4cb0"},"headline":"How We Compiled a Golang Database in the Browser Using WebAssembly","datePublished":"2019-11-16T00:00:00+00:00","dateModified":"2025-03-04T13:43:57+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/"},"wordCount":1933,"publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","keywords":["Go","Hackathon"],"articleSection":["Engineering"],"inLanguage":"ko-KR"},{"@type":"WebPage","@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/","url":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/","name":"How We Compiled a Golang Database in the Browser","isPartOf":{"@id":"https:\/\/www.pingcap.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage"},"image":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage"},"thumbnailUrl":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","datePublished":"2019-11-16T00:00:00+00:00","dateModified":"2025-03-04T13:43:57+00:00","description":"In this post, we will dive deep into how and why we built an in-browser database, and you'll know how to reproduce it yourself.","breadcrumb":{"@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#breadcrumb"},"inLanguage":"ko-KR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/"]}]},{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#primaryimage","url":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","contentUrl":"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg","width":2000,"height":667},{"@type":"BreadcrumbList","@id":"https:\/\/www.pingcap.com\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.pingcap.com\/"},{"@type":"ListItem","position":2,"name":"How We Compiled a Golang Database in the Browser Using WebAssembly"}]},{"@type":"WebSite","@id":"https:\/\/www.pingcap.com\/#website","url":"https:\/\/www.pingcap.com\/","name":"\ud2f0DB","description":"TiDB | SQL at Scale","publisher":{"@id":"https:\/\/www.pingcap.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.pingcap.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ko-KR"},{"@type":"Organization","@id":"https:\/\/www.pingcap.com\/#organization","name":"PingCAP","url":"https:\/\/www.pingcap.com\/","logo":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/","url":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","contentUrl":"https:\/\/static.pingcap.com\/files\/2021\/11\/pingcap-logo.png","width":811,"height":232,"caption":"PingCAP"},"image":{"@id":"https:\/\/www.pingcap.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/facebook.com\/pingcap2015","https:\/\/x.com\/PingCAP","https:\/\/linkedin.com\/company\/pingcap","https:\/\/youtube.com\/channel\/UCuq4puT32DzHKT5rU1IZpIA"]},{"@type":"Person","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/c4880334852b40d445e34c2c608a4cb0","name":"Joshua Zhou","image":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.pingcap.com\/#\/schema\/person\/image\/","url":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","contentUrl":"https:\/\/static.pingcap.com\/files\/2022\/10\/17234942\/avatar.jpg","caption":"Joshua Zhou"},"url":"https:\/\/www.pingcap.com\/ko\/blog\/author\/joshua-zhou\/"}]}},"grav_blocks":false,"card_markup":"<a class=\"card-resource bg-white\" href=\"https:\/\/www.pingcap.com\/ko\/blog\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly\/\"><div class=\"card-resource__image-container\"><img class=\"card-resource__image\" alt=\"how-we-compiled-a-golang-database-in-the-browser-using-webassembly.png\" src=\"https:\/\/static.pingcap.com\/files\/2019\/11\/how-we-compiled-a-golang-database-in-the-browser-using-webassembly.jpg\" loading=\"lazy\" width=2000 height=667 \/><\/div><div class=\"card-resource__content-container\"><div class=\"card-resource__content-head\"><div class=\"card-resource__category\">Engineering<\/div><\/div><h5 class=\"card-resource__title\">How We Compiled a Golang Database in the Browser Using WebAssembly<\/h5><\/div><\/a>","_links":{"self":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/655","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/users\/70"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/comments?post=655"}],"version-history":[{"count":7,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/655\/revisions"}],"predecessor-version":[{"id":25457,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/posts\/655\/revisions\/25457"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media\/666"}],"wp:attachment":[{"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/media?parent=655"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/categories?post=655"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pingcap.com\/ko\/wp-json\/wp\/v2\/tags?post=655"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}