<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Michael Mior on Medium]]></title>
        <description><![CDATA[Stories by Michael Mior on Medium]]></description>
        <link>https://medium.com/@michaelmior?source=rss-52936e309fc5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*v6o5U-l_XmI7zM9QYd6Tdg.png</url>
            <title>Stories by Michael Mior on Medium</title>
            <link>https://medium.com/@michaelmior?source=rss-52936e309fc5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 23:31:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@michaelmior/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Game of Firsts]]></title>
            <link>https://medium.com/@michaelmior/game-of-firsts-a431ba156a30?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a431ba156a30</guid>
            <category><![CDATA[game-development]]></category>
            <category><![CDATA[text-to-speech]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 16:25:53 GMT</pubDate>
            <atom:updated>2024-08-07T16:25:53.165Z</atom:updated>
            <content:encoded><![CDATA[<p>A lot of things seem to serve to unintentionally <a href="https://xkcd.com/356/">nerd snipe</a> me.<br>Over the weekend, a friend introduced me to <a href="https://www.initialsgame.com/">The Initials Game</a>.<br>The gist of it is that you are given two letters and a series of clues.<br>The goal is to guess a two-word phrase that starts with the given letters.<br>It’s a pretty entertaining game and I wondered how easy it would be to generate such puzzles.</p><p>The next day I was able to hack something together in a couple hours that actually worked reasonably well. The entire thing is based around <a href="https://ollama.com/">Ollama</a> since I wanted to be able to run locally. I started off with a system prompt</p><pre>You are a puzzle designer who is going to help create a word puzzle. Any pieces of the puzzle you generate should be short and simple and easily understandable to the average English-speaking adult anywhere in the world.</pre><p>Then I split the remainder into two prompts. Given two letters, the first prompt asks for a phrase to be used in the puzzle.</p><pre>I will give you two letters and then you will think of a very simple two word phrase that starts with those two letters. For example, if I give you the letters C and F, you might pick Correctional Facility. For the letters P and T, you might select Party Trick. Respond with only the two word phrase and nothing else. The letters are {letter1} and {letter2}.</pre><p>The second prompt asks for clues given this phrase.</p><pre>I am going to give you a two word phrase and I would like you to devise five very short clues (three or four words maximum) that will help someone guess the phrase. The first clue should be very vague and subsequent clues should get increasingly specific. Do not use any of the words from the phrase anywhere in any of the clues or elsewhere in your response. Output should be a simple numbered list in Markdown format. The two word phrase is &quot;{phrase}&quot;.</pre><p>I later made a few further tweaks to these prompts, but this is the gist of it.<br>At this point, I was sometimes getting some reasonable phrases, but sometimes phrases that didn’t make sense at all. Sometimes the letters in the phrase didn’t match the letters that were asked for. But the biggest problem was that sometimes the phrases generated were pretty nonsensical, for example, “Bird Age.” To solve this, I ended up using <a href="https://ngrams.dev/">NGRAMS</a> which is a REST API for querying the Google Books n-gram dataset. This allows easily checking for how common the phrase is. For example, “Bird Age”, appears only 269 times, while the much more reasonable phrase “Business Association” appears 200,334 times. The solution was to generate multiple phrases until one meets an arbitrarily-determined threshold of popularity.</p><p>Up until this point, I was just printing all the puzzles out via text.<br>But it’s a bit more fun to have them read out. I ended up using <a href="https://github.com/synesthesiam/opentts">OpenTTS</a> to generate audio from the puzzles. One unexpected problem is that coqui-tts, the actually speech system used, seems to have a real problem pronouncing letters when just written out as single letters. For example, <a href="https://michael.mior.ca/blog/game-of-firsts/ae.mp3">here’s what I got</a> when I tried to have it say “The letters are E and A.”</p><p>To solve this, I wrote out phonetic spellings of each letter, tweaking until each one sounded right. If I instead generate audio for “The letters are Eeh and Ae,” I get <a href="https://michael.mior.ca/blog/game-of-firsts/">much more reasonable output</a>.</p><p>I first started with requiring the user to specify which letters to generate.<br>When switching to randomly picking letters, a uniform distribution doesn’t really work well. English words aren’t randomly distributed so it makes sense to match this frequency. To do this, I instead pick letters randomly but weighted based on the frequency of the first letters of English words. To avoid picking a lot of double letters, I also halve the probability of the first letter when generating the second letter.</p><p>Finally, <a href="https://michael.mior.ca/blog/game-of-firsts/">here’s an example of a complete puzzle</a>. The code is available <a href="https://github.com/michaelmior/game-of-firsts">on GitHub</a>. Not sure if I’ll keep working on this project further, but it’s pretty impressive what you can quickly accomplish these days with a bit of use of AI.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a431ba156a30" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LLMs for Schema Augmentation]]></title>
            <link>https://medium.com/@michaelmior/llms-for-schema-augmentation-b7d979be0a4d?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/b7d979be0a4d</guid>
            <category><![CDATA[chatgpt]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[json-schema]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Tue, 18 Jul 2023 16:52:52 GMT</pubDate>
            <atom:updated>2023-07-18T16:52:52.065Z</atom:updated>
            <content:encoded><![CDATA[<p>I have recently been experimenting with the use of large language models (LLMs) to augment JSON Schemas with useful features. While ChatGPT gets most of the press, there are many other LLMs that are specifically designed to work with code. The idea is that these LLMs can be used to augment incomplete schemas with additional useful information.</p><p>Consider an example schema such as the one below. This is a basic schema which might be created from an automated schema mining process. For such a small schema, this is probably sufficient information to tell you useful things about the dataset.</p><pre>{<br>  &quot;type&quot;: &quot;object&quot;,<br>  &quot;properties&quot;: {<br>    &quot;name&quot;: {&quot;type&quot;: &quot;string&quot;},<br>    &quot;address&quot;: {&quot;type&quot;: &quot;string&quot;}<br>  }<br>}</pre><p>However, as schemas grow in size and complexity, additional metadata can be useful. For example, JSON Schemas can contain a description attribute which provides a natural language description of each property. To generate a value for such a property, we can prompt the LLM with a prefix of the schema such as the following.</p><pre>{<br>  &quot;type&quot;: &quot;object&quot;,<br>  &quot;properties&quot;: {<br>    &quot;name&quot;: {&quot;type&quot;: &quot;string&quot;},<br>    &quot;address&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;description&quot;: &quot;</pre><p>From here, we just need to continue generating tokens until we get to a closing quote. This approach was borrowed from <a href="https://github.com/1rgs/jsonformer">Jsonformer</a> which uses a similar approach to induce LLMs to generate structured output. Continuing to do so for each property using <a href="https://huggingface.co/replit/replit-code-v1-3b">Replit’s code LLM</a> gives the following output:</p><pre>{<br>  &quot;type&quot;: &quot;object&quot;,<br>  &quot;properties&quot;: {<br>    &quot;name&quot;: {<br>      &quot;type&quot;: &quot;string&quot;,<br>      &quot;description&quot;: &quot;The name of the item&quot;<br>    },<br>    &quot;address&quot;: {<br>      &quot;type&quot;: &quot;string&quot;,<br>      &quot;description&quot;: &quot;The address of the person&quot;<br>    }<br>  }<br>}</pre><p>While it’s not perfect, and not obviously useful for such a small schema, I think the results are promising. I’ve also tried several other schema formats such as <a href="https://github.com/colinhacks/zod">Zod</a>, <a href="https://www.typescriptlang.org/docs/handbook/2/objects.html">Typescript object types</a>, and <a href="https://docs.pydantic.dev/latest/">Pydantic</a>. A lot more experimentation is required with these different formats and various LLMs to see which works best. So far, I’m pretty pleased with results. The code for this post <a href="https://github.com/michaelmior/annotate-schema">is available on GitHub</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b7d979be0a4d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Zotero reMarkable sync]]></title>
            <link>https://medium.com/@michaelmior/zotero-remarkable-sync-b9df26837914?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/b9df26837914</guid>
            <category><![CDATA[remarkable]]></category>
            <category><![CDATA[heroku]]></category>
            <category><![CDATA[zotero]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Fri, 23 Mar 2018 00:00:00 GMT</pubDate>
            <atom:updated>2018-07-31T01:52:48.403Z</atom:updated>
            <content:encoded><![CDATA[<p>I’ve really been enjoying my <a href="https://remarkable.com/">reMarkable</a> tablet the past several months. (I wrote <a href="https://michael.mior.ca/blog/remarkable-review/">a short review</a> last year if you care to see a few more details). One of the biggest gripes I have about the device is that it can be a pain to get documents on it. There’s currently no web app so the only choice is desktop apps for Windows and macOS or a mobile app for Android. Once I saw someone had released a <a href="https://github.com/splitbrain/ReMarkableAPI">reMarkable API</a> on GitHub, I knew I would have to find some way to ease my pain.</p><p>I use <a href="https://www.zotero.org/">Zotero</a> to manage my paper references and unsurprisingly, reading papers is one of the primary uses of my reMarkable. I decided to figure out a way I could have new papers I wanted to read in Zotero show up on my reMarkable. Using the reMarkable and Zotero APIs, this proved to be a fairly straightforward weekend project. You can find the result <a href="https://github.com/michaelmior/zotero-remarkable">on GitHub</a>.</p><p>To get started, there are simply a few environment variables that need to be set which are detailed in the README. Then once the script is run, it will look for items in a Zotero collection, download their attachments, and upload them to the reMarkable API. Putting this script into a cron job means that my papers are now synchronized regularly. I’m currently hosting this for myself for free on Heroku using the <a href="https://devcenter.heroku.com/articles/scheduler">scheduler add-on</a> to run the job. I disabled any web process (heroku ps:scale web=0) so the only thing that runs is the cron job. Since the cron job is quite quick to run, it falls well within the free usage limits of Heroku.</p><p>I may decide to add a <a href="https://devcenter.heroku.com/articles/heroku-button">Heroku button</a> to the repository in the future, but it’s fairly straightforward to configure manually. Just set the required environment variables, disable the web dyno, and set up the cron job with the scheduler. Hope this ends up being useful to someone else!</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/zotero-remarkable-sync/"><em>michael.mior.ca</em></a><em> on March 23, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9df26837914" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LaTeX Skeleton]]></title>
            <link>https://medium.com/@michaelmior/latex-skeleton-a4f064b1847f?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a4f064b1847f</guid>
            <category><![CDATA[latex]]></category>
            <category><![CDATA[writing]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Fri, 02 Mar 2018 00:00:00 GMT</pubDate>
            <atom:updated>2018-03-16T14:07:29.581Z</atom:updated>
            <content:encoded><![CDATA[<p>A repeated task I run into when I start working on a new paper is the laying out the initial structure of the repository to store the paper text. I recently pushed <a href="https://github.com/michaelmior/latex-skeleton">a simple skeleton</a> that I use a starting point to GitHub. There’s nothing really fancy here, but it’s a good starting point. I use <a href="https://mg.readthedocs.io/latexmk.html">latexmk</a> to build all my documents since it takes care of running BibTeX among other things. One of the nice other things is that it will automatically try to use make to build any missing files. The repository basically consists of a Makefile that generates the paper along with a simple LaTeX skeleton and an empty BibTeX file. Hope it might be helpful to someone else!</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/latex-skeleton/"><em>michael.mior.ca</em></a><em> on March 2, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a4f064b1847f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reinforcement learning for Las Vegas]]></title>
            <link>https://medium.com/@michaelmior/reinforcement-learning-for-las-vegas-a84cfc036bb?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a84cfc036bb</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[board-games]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[neural-networks]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Sun, 04 Feb 2018 00:00:00 GMT</pubDate>
            <atom:updated>2018-02-05T00:45:46.811Z</atom:updated>
            <content:encoded><![CDATA[<p>During a department board games night, we were playing <a href="https://boardgamegeek.com/boardgame/117959/las-vegas">Las Vegas</a> when a fellow player remarked that he wondered how an AI for the game would perform. Since I had been meaning to spend some time learning to implement neural network techniques, this seemed like a great opportunity. One of the first things that came to mind was a <a href="https://arxiv.org/abs/1312.5602">paper</a> from the DeepMind team on using deep neural networks to implement a variant <a href="https://en.wikipedia.org/wiki/Q-learning">Q-learning</a>. The gist behind classical Q-learning is maintaining a table with the expected utility of a particular action in a given state. This table is updated while the game is played based on the observed rewards.</p><p>The idea behind deep Q-learning is to use a neural network to replace the table which is traditionally used. One of the big advantages is that it’s possible to handle much larger state-action spaces using a neural network. The first step was to decide how to represent the game state. For anyone not familiar with Las Vegas, <a href="http://www.yucata.de/en/Rules/LasVegas">Yucata</a> has a good overview of the rules and a mechanism for playing online. The short version is that players take turns rolling dice and placing them on differently numbered casinos in an attempt to get the highest cash reward.</p><p>I first built a simple class structure for the game in Python to represent all the game state and stubbed out a couple functions to implement the game logic. The next step was to decide how the state was going to fed into the network. In the original deep Q-learning paper, the authors used a <a href="https://en.wikipedia.org/wiki/Convolutional_neural_network">convolutional neural network</a> to feed in frames from gameplay video. I instead chose to explicit represent the state using the following values:</p><ul><li>Number of players in the game</li><li>Current game round number</li><li>Cash currently held by each player</li><li>Number of dice currently on each casino</li><li>Money available at each casino</li><li>Number of dice of each value in the current roll</li></ul><p>Explicitly representing the state also resulted in a different structure for the network itself. The input layer simply received a normalized vector of the state values above. The second fully-connected layer was simply half the size of the first layer. Both of the first two layers used a <a href="https://en.wikipedia.org/wiki/Rectifier_(neural_networks">ReLU</a>) activation function. The final output layer was also fully connected but with a linear activation function and a size of six to represent the choice of each possible die. After training the AI against 4 random opponents, the AI was able to win around 50% of games which I was pretty happy with given the inherent randomness of the game. However, the evaluation was by no means robust and something that definitely needs to be improved upon.</p><p>I later implemented hyperparameter optimization using the <a href="https://github.com/hyperopt/hyperopt">Hyperopt</a> library. After much more training, I tried to optimize the reward values, <a href="https://en.wikipedia.org/wiki/Q-learning#Discount_factor">discount factor</a>), and other parameters specific to deep Q-learning. This led me to change the kernel initializer to <a href="https://keras.io/initializers/#lecun_uniform">LeCun uniform</a>, the activation function of the first two layers to a <a href="https://keras.io/activations/#sigmoid">sigmoid function</a>, and the optimization algorithm from <a href="https://keras.io/optimizers/#rmsprop">RMSprop</a> to <a href="https://keras.io/optimizers/#adam">Adam</a>. This was mostly for a bit more fun although it did seem to provide about a 20% performance improvement on some simple evaluations I tried.</p><p>Since this is just a fun side project, one of the next things on my agenda is to implement a UI using <a href="http://boardgame.io/">boardgame.io</a> so I can get a sense of how the AI “feels.” All in all, this was a pretty fun project. The source code is available <a href="https://github.com/michaelmior/lasvegas">on GitHub</a> for anyone who wants to play with it.</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/reinforcement-learning-for-las-vegas/"><em>michael.mior.ca</em></a><em> on February 4, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a84cfc036bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[reMarkable review]]></title>
            <link>https://medium.com/@michaelmior/remarkable-review-ff8ac453bdf4?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/ff8ac453bdf4</guid>
            <category><![CDATA[remarkable]]></category>
            <category><![CDATA[e-ink]]></category>
            <category><![CDATA[tablets]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Tue, 31 Oct 2017 00:00:00 GMT</pubDate>
            <atom:updated>2017-10-31T18:37:00.221Z</atom:updated>
            <content:encoded><![CDATA[<p>As an academic, I spend a lot of time reading papers. I generally hate the idea of printing out a paper for a one-time read since it feels wasteful, but I also don’t enjoy the reading experience of viewing papers on a desktop screen. I’ve always liked the look of the <a href="https://www.sony.com/electronics/digital-paper-notepads/dpt-rp1">Sony Digital Paper</a> that my advisor uses but it’s rather expensive and not easy to find. I used a <a href="http://www.samsung.com/consumer/mobile-devices/tablets/others/GT-P5100ZWAXEF/">Samsung Galaxy Tab 3</a> for several years which freed me from my desk and made note-taking easier, but still wasn’t as nice as paper.</p><p>This is why I was excited to hear about the <a href="https://remarkable.com/">reMarkable</a>. The stated goal of reMarkable is to be a tablet for paper lovers. The cost is still fairly significant for a grad student, and I actually cancelled my first preorder before changing my mind and committing. While I did experience shipping delays that seem inevitable with crowd funded projects, I finally got my device this past Thursday.</p><p>So far, I’m pretty impressed by the hardware and the writing experience. There’s definitely no perceivable latency when writing and most other actions happen quite quickly as well. The software still needs a lot of work (especially the <a href="https://play.google.com/store/apps/details?id=com.remarkable.mobile">Android app</a>) and I’ve had some issues with syncing not working correctly. My biggest disappointment so far, is that the Wi-Fi on the device doesn’t currently support networks which use usernames and passwords for authentication, which means I can’t use wireless connectivity in the office. This is a pretty big drawback, but there’s already a promise to fix this in a future software update. Overall, I’m looking forward to seeing what happens.</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/remarkable-review/"><em>michael.mior.ca</em></a><em> on October 31, 2017.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ff8ac453bdf4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Benchmarking ScyllaDB]]></title>
            <link>https://medium.com/@michaelmior/benchmarking-scylladb-9fae0413607a?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/9fae0413607a</guid>
            <category><![CDATA[benchmark]]></category>
            <category><![CDATA[cassandra]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Sat, 11 Mar 2017 00:00:00 GMT</pubDate>
            <atom:updated>2017-10-10T14:34:54.359Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="http://www.scylladb.com/">ScyllaDB</a> is an alternative to <a href="https://cassandra.apache.org/">Apache Cassandra</a> which claims to have 10x higher throughput than Cassandra while remaining the same positive properties of scalability and ease of use. Scylla functions as a drop-in replacement for Cassandra applications using <a href="https://docs.datastax.com/en/cql/3.1/index.html">CQL</a> (currently with some <a href="http://www.scylladb.com/technology/status/">disparity in features</a>). Fortunately, my <a href="https://michael.mior.ca/projects/NoSE/">past work</a> in NoSQL schema design for Cassandra only makes use of features supported by Scylla.</p><p>I’ve been meaning to do my own tests on Scylla for a while, but recently <a href="https://cs.uwaterloo.ca/~jimmylin/">a faculty member</a> shared about Scylla on our group’s Slack channel and suggested I share some results. I decided to run the same set of experiments I used to evaluate a schema I designed for the <a href="http://rubis.ow2.org/">RUBiS</a> online auction benchmark. First, several column families are created and loaded with data. Next, the different types of transactions in the RUBiS benchmark are executed while measuring the response time. Before I continue, first a disclaimer that the methodology behind the results is not as rigorous as it could be, but still leaves me skeptical of some of the claims made by Scylla.</p><p>These experiments were run using single node installations of Cassandra 3.0.9 and Scylla 1.6.1. While this is not a typical setup, much of the reasons for performance improvements claimed by Scylla (e.g. lock-free data structures and improved memory management) should still manifest themselves on a single node. One of the nice things about Scylla is the scylla_setup command that attempts to configure the OS for optimal performance including benchmarking the disk storing the data directory. This configuration was used for both Scylla and Cassandra and otherwise the default settings were used for both systems.</p><p>The first striking difference is that the on-disk size of the data for Scylla (9.8 GB) is nearly twice that of Cassandra (5.2 GB). Despite this, there was not a large difference in load times with Cassandra taking 3 hours 43 minutes and Scylla taking 3 hours 59 minutes. Below is a graph with the write throughput of the SSD storing the data files in each case. Scylla seems to push the drive much harder but it’s able to keep up.</p><p><strong>Update</strong>: After looking at the number of keys in each table for both Scylla and Cassandra, it seems as though Scylla was storing significantly more data. Stay tuned for updates on resolving this issue.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/0*kYo1xNCLpY9oFruy.png" /></figure><p>Finally, the results of running the actual benchmark. RUBiS consists of a number of “interactions” which corresponds to user requests for web pages. The graph below shows the top eight interactions by frequency and the average response times for both Cassandra and Scylla. I won’t go into a detailed analysis here, but the performance claims made by Scylla don’t seem to play out here.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*1YspXMIHMLRX_eoI.png" /></figure><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/scylladb-benchmarks/"><em>michael.mior.ca</em></a><em> on March 11, 2017.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9fae0413607a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Calcite adapter for Apache Cassandra]]></title>
            <link>https://medium.com/@michaelmior/a-calcite-adapter-for-apache-cassandra-db11853c8fe8?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/db11853c8fe8</guid>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[cassandra]]></category>
            <category><![CDATA[calcite]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Sat, 20 Feb 2016 00:00:00 GMT</pubDate>
            <atom:updated>2017-10-10T14:36:28.377Z</atom:updated>
            <content:encoded><![CDATA[<p>For those not familiar, <a href="https://calcite.apache.org/">Apache Calcite</a> is a generic SQL query optimizer which can execute SQL queries over multiple backend data sources. This is a powerful concept because it allows complex queries to be executed over sources which provide much simpler interfaces from <a href="https://calcite.apache.org/apidocs/org/apache/calcite/adapter/csv/package-summary.html">CSV files</a> to <a href="https://calcite.apache.org/apidocs/org/apache/calcite/adapter/mongodb/package-summary.html">MongoDB</a>. Calcite is also leveraged as the cost-based-optimizer framework for the <a href="https://cwiki.apache.org/confluence/display/Hive/Cost-based+optimization+in+Hive">Apache Hive</a> data warehouse.</p><p>Much of my PhD research has revolved around generating optimized schemas for NoSQL databases such as <a href="https://cassandra.apache.org/">Apache Cassandra</a>. (For a proof-of-concept tool, check out the <a href="https://github.com/michaelmior/NoSE">NoSQL Schema Evaluator</a>.) On discovering calcite, this seemed like a good fit with my work. One of the challenges with using NoSQL databases for complex queries is the necessity of working within the restrictions set by the query language. In previous work, I built a very simple query execution on top of Cassandra designed to execute a predefined set of query plans. Leveraging Calcite, it is possible to execute a very complete <a href="https://calcite.apache.org/docs/reference.html">dialect of SQL</a> on top of any defined data source (which calcite calls “adapters”).</p><p>Unfortunately, Calcite did not already have an adapter for Cassandra. Fortunately, writing an adapter is a fairly straightforward process, so I decided to take this on. The simplest possible implementation of an adapter provides a set of tables along with a scan operator to retrieve all the rows in the tables. While this is sufficient to enable Calcite to perform query execution, scanning a table in Cassandra is <a href="http://www.myhowto.org/bigdata/2013/11/04/scanning-the-entire-cassandra-column-family-with-cql/">very inefficient</a>. This is a result of the fact that partitions in a Cassandra table are commonly distributed across nodes via hash partitioning. While it is possible to retrieve all rows, they will be produced in a random order and the query will need to contact all nodes in the database. Assuming that the query the user wants to issue does not need to touch all rows in a table, it is possible to use filtering in the Cassandra Query Language (<a href="http://docs.datastax.com/en/cql/3.3/cql/cql_reference/cqlReferenceTOC.html">CQL</a>) to push filtering down to Cassandra.</p><p>The current version of the adapter also supports exploiting the native sort order of Cassandra tables by <a href="https://docs.datastax.com/en/cql/3.0/cql/ddl/ddl_compound_keys_c.html">clustering key</a>. There is still a lot of work to be done, but an initial version of this adapter should be shipped in Calcite 1.7.0. Until the release, you’ll have to compile <a href="https://github.com/apache/calcite/">from source</a>. A quick set of commands to get things running is below.</p><pre>$ git clone https://github.com/apache/calcite.git $ cd calcite $ mvn install $ ./sqlline sqlline&gt; !connect jdbc:calcite:model=path/to/cassandra/model.json admin admin</pre><p>At this point you can write SQL queries which reference your Cassandra tables. Note that table names need to be quoted and there will likely be some failures with certain query patterns. You can view the proposed plan for a query by prefixing it with EXPLAIN PLAN FOR in the sqlline shell. This will show whether the query is able to exploit filtering or sorting directly in CQL. This is a long way from making Cassandra a viable data warehouse, but it may be helpful for performing occasional analytical queries without needing to write a significant amount of code.</p><p>Calcite 1.7.0 has now been <a href="https://calcite.apache.org/docs/history.html#v1-7-0">released</a> which includes the Cassandra adapter. In addition to what was discussed above, the adapter also now automatically recognizes <a href="https://www.datastax.com/dev/blog/new-in-cassandra-3-0-materialized-views">materialized views</a>. <a href="https://calcite.apache.org/docs/cassandra_adapter.html">Documentation</a> is available on the Calcite website.</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/calcite-cassandra-adapter/"><em>michael.mior.ca</em></a><em> on February 20, 2016.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=db11853c8fe8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automated Testing of Dotfiles]]></title>
            <link>https://medium.com/@michaelmior/automated-testing-of-dotfiles-142db61f5f67?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/142db61f5f67</guid>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[dotfiles]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Thu, 24 Sep 2015 00:00:00 GMT</pubDate>
            <atom:updated>2017-10-10T14:37:18.471Z</atom:updated>
            <content:encoded><![CDATA[<p>Several years ago I started managing my dotfiles based on Zach Holman’s <a href="https://github.com/holman/dotfiles">dotfiles repo</a>. His setup is quite nice and I found it relatively easy to adapt to my own purposes. My workflow generally consisted of making a bunch of local changes until I was happy and then pushing to my own <a href="https://github.com/michaelmior/dotfiles">GitHub fork</a>.</p><p>The big problem I eventually found is that I wasn’t fully capturing the correct steps to reproduce my environment. Every time that I tried to install my dotfiles on a new machine, I would be met with several errors that I would eventually resolve. The fix would not always result in something which was reproducible on another machine. I wanted a solution that would let me automatically test that my dotfiles would cleanly install every time I pushed to GitHub, so I turned to <a href="https://www.docker.com/">Docker</a>.</p><p>Traditional CI services would have been a bit of a pain to use with all the packages that needed to be installed. <a href="https://hub.docker.com/r/michaelmior/dotfiles/">Docker Hub</a> made things nice and easy. My <a href="https://github.com/michaelmior/dotfiles/blob/a9eae90d466958948a53b3b583d69eba844ed8f7/Dockerfile">Dockerfile</a> simply installs the necessary OS packages, adds a new user and then tries to run my install script. I currently don’t have any other testing other than to ensure that the script exits without error, but this has already saved me a lot of trouble.</p><p><strong>Update</strong>: I have since switched to using <a href="https://travis-ci.org/michaelmior/dotfiles">Travis CI</a> as I do with my other projects. It turns out this is easier than I expected. I still haven’t explicitly added any tests but even being able to confirm that the installation steps succeed is useful to ensure nothing breaks.</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/automated-testing-of-dotfiles/"><em>michael.mior.ca</em></a><em> on September 24, 2015.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=142db61f5f67" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apache Cassandra benchmarking]]></title>
            <link>https://medium.com/@michaelmior/apache-cassandra-benchmarking-93e8171cb114?source=rss-52936e309fc5------2</link>
            <guid isPermaLink="false">https://medium.com/p/93e8171cb114</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[nosql]]></category>
            <category><![CDATA[benchmark]]></category>
            <category><![CDATA[cassandra]]></category>
            <dc:creator><![CDATA[Michael Mior]]></dc:creator>
            <pubDate>Thu, 21 Aug 2014 00:00:00 GMT</pubDate>
            <atom:updated>2017-10-10T14:38:04.458Z</atom:updated>
            <content:encoded><![CDATA[<p>I was recently trying to run some benchmarks against <a href="http://cassandra.apache.org/">Apache Cassandra</a> on <a href="http://aws.amazon.com/ec2/">EC2</a> since unfortunately the servers I had in our machine room were destroyed in a fire. For all my local testing, I used a single instance running on my desktop machine, but I wanted to ramp things up for the real benchmarks and use three nodes. Since my workload is read-only and the dataset is fairly small, I also wanted a replication factor of three so each node would have a copy of all the data.</p><p>My first attempt to load all this data was to follow <a href="http://www.datastax.com/documentation/cql/3.0/cql/cql_using/update_ks_rf_t.html">some documentation</a> provided by DataStax. Their suggestion was to use ALTER KEYSPACE in CQL to change the replication factor, and then simply run nodetool repair on each node. However, I found that running repair on just one node took several hours for a modest-sized amount of data (~2GB). This was a pretty big time sink as I wanted to able to quickly spin up and down a cluster for testing.</p><p>Next I tried changing the configured replication factor locally before exporting the data. I then simply copied the data to all nodes in the cluster and tried to start them as normal. This created some weird conflicts as nodes seemed to be confused about who owned what portion of data.</p><p>Finally, I simply loaded up the data set on a single node and configured a replication factor of three. I then started each node in sequence and the auto bootstrapping process took care of copying the entire dataset to each node in the cluster. This whole process was complete in less than half an hour. This approach wouldn’t really work in a production setting since it assumes the node has no existing data (although if you can afford to bring a node offline for a while, I suppose that it might work). In any case, this solution worked great for me and hopefully someone else finds this useful.</p><p><em>Originally published at </em><a href="https://michael.mior.ca/blog/cassandra-benchmarking/"><em>michael.mior.ca</em></a><em> on August 21, 2014.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=93e8171cb114" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>