tag:blogger.com,1999:blog-17997863435499530722024-03-13T11:24:29.414-07:00Kalimotxo CodingGustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.comBlogger36125tag:blogger.com,1999:blog-1799786343549953072.post-62637419737518994842022-10-24T12:36:00.003-07:002022-10-24T12:38:48.327-07:00Using Node.js AsyncLocalContext to store tracing information<p>Usually when building Node.js services you want to be able to include in your logs some identifier (trace ID or user ID) that is shared for all the logs for the same request or event handler. That way when you need to debug a failed request you can easily filter all the logs belonging to the same request.<br /></p><p>This is very easy to implement in other execution environments thanks to the concept of thread-local variables where you can store some information for the duration of a task in a storage that is specific of the thread executing that task. For example in Java you can make use of the ThreadLocal class to store information that then will be available for the rest of the execution of that thread. </p><p><span class="hljs-keyword" style="box-sizing: border-box; color: #63b175; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: 600; white-space: pre-wrap;"><span> </span>private</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;"> </span><span class="hljs-keyword" style="box-sizing: border-box; color: #63b175; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: 600; white-space: pre-wrap;">static</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;"> ThreadLocal<RequestContext> context = </span><span class="hljs-keyword" style="box-sizing: border-box; color: #63b175; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: 600; white-space: pre-wrap;">new</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;"> </span><span class="hljs-title class_" style="box-sizing: border-box; color: #267438; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: bold; white-space: pre-wrap;">ThreadLocal</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;"><>();
</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;">context.set(</span><span class="hljs-keyword" style="box-sizing: border-box; color: #63b175; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: 600; white-space: pre-wrap;">new</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;"> Request</span><span class="hljs-title class_" style="box-sizing: border-box; color: #267438; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; font-weight: bold; white-space: pre-wrap;">Context</span><span style="background-color: #fafafa; font-family: "Source Code Pro", Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; font-size: 14px; white-space: pre-wrap;">(requestId));</span><br /><br />This is not directly applicable to Node.js because all the tasks of a process are executed asynchronously in the same thread, but there is some similar functionality provided by the AsyncLocalContext cass. In this case the context is not per thread but per call stack, so two flows of execution will have different stacks and different values for the variables stored.</p><p>Let's look at it with an example using the express nodejs framework.</p><p><br /></p><p>First thing you want to do is assign a new context to each request and assign it to the current asyncLocalStorage. This can be done with a express middleware to make it transparent for all the requests:</p><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div style="line-height: 18px;"><span style="color: #af00db;">import</span> { <span style="color: #001080;">AsyncLocalStorage</span> } <span style="color: #af00db;">from</span> <span style="color: #a31515;">'node:async_hooks'</span>;</div><span style="color: blue;">
const</span> <span style="color: #0070c1;">asyncLocalStorage</span> = <span style="color: blue;">new</span> <span style="color: #267f99;">AsyncLocalStorage</span>();</div><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><br /></div><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #0070c1;">app</span>.<span style="color: #795e26;">use</span>((<span style="color: #001080;">_req</span>, <span style="color: #001080;">res</span>, <span style="color: #795e26;">next</span>) <span style="color: blue;">=></span> {</div><div> <span style="color: blue;">const</span> <span style="color: #0070c1;">context</span> = { <span style="color: #001080;">traceId</span><span style="color: #001080;">:</span> <span style="color: #267f99;">crypto</span>.<span style="color: #795e26;">randomUUID</span>(), <span style="color: #001080;">begin</span><span style="color: #001080;">:</span> <span style="color: #267f99;">Date</span>.<span style="color: #795e26;">now</span>() };</div><div> <span style="color: #0070c1;">asyncLocalStorage</span>.<span style="color: #795e26;">run</span>(<span style="color: #0070c1;">context</span>, <span style="color: blue;">async</span> () <span style="color: blue;">=></span> {</div><div> <span style="color: #795e26;">next</span>();</div><div> });</div><div>});</div></div><p>In this example we are associating a new UUID traceId and the begin time with every request.</p><p>Next you want your logger to add the information from the context when generating a log. In this example we are using winston library for logging and a custom formatter to add the context data:</p><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: blue;">const</span> <span style="color: #795e26;">addLoggingContext</span> = <span style="color: #267f99;">winston</span>.<span style="color: #795e26;">format</span>(<span style="color: #001080;">info</span> <span style="color: blue;">=></span> {</div><div> <span style="color: blue;">const</span> <span style="color: #0070c1;">context</span> = <span style="color: #0070c1;">asyncLocalStorage</span>.<span style="color: #795e26;">getStore</span>() <span style="color: #af00db;">as</span> <span style="color: #267f99;">any</span>;</div><div> <span style="color: #af00db;">return</span> <span style="color: #0070c1;">context</span> ? { ...<span style="color: #001080;">info</span>, ...<span style="color: #0070c1;">context</span>, <span style="color: #001080;">duration</span><span style="color: #001080;">:</span> <span style="color: #267f99;">Date</span>.<span style="color: #795e26;">now</span>() - <span style="color: #0070c1;">context</span>.<span style="color: #001080;">begin</span> } : <span style="color: #001080;">info</span>;</div><div>});</div><br /><div><span style="color: #0070c1;">logger</span>.<span style="color: #795e26;">add</span>(<span style="color: blue;">new</span> <span style="color: #267f99;">winston</span>.<span style="color: #0070c1;">transports</span>.<span style="color: #267f99;">Console</span>({</div><div> <span style="color: #001080;">format</span><span style="color: #001080;">:</span> <span style="color: #267f99;">winston</span>.<span style="color: #795e26;">format</span>.<span style="color: #795e26;">combine</span>(</div><div> <span style="color: #795e26;">addLoggingContext</span>(),</div><div> <span style="color: #267f99;">winston</span>.<span style="color: #795e26;">format</span>.<span style="color: #795e26;">simple</span>(),</div><div> ),</div><div>}));</div></div><p>With that in place we can add some logs. We can add some basic ones in the middleware and some in the request processing for example:</p><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div><span style="color: #0070c1;">app</span>.<span style="color: #795e26;">use</span>((<span style="color: #001080;">_req</span>, <span style="color: #001080;">res</span>, <span style="color: #795e26;">next</span>) <span style="color: blue;">=></span> {</div><div> <span style="color: #001080;">res</span>.<span style="color: #795e26;">on</span>(<span style="color: #a31515;">'finish'</span>, () <span style="color: blue;">=></span> {</div><div> <span style="color: #0070c1;">logger</span>.<span style="color: #795e26;">info</span>(<span style="color: #a31515;">'End request'</span>);</div><div> });</div><div> <span style="color: blue;">const</span> <span style="color: #0070c1;">context</span> = { <span style="color: #001080;">traceId</span><span style="color: #001080;">:</span> <span style="color: #267f99;">crypto</span>.<span style="color: #795e26;">randomUUID</span>(), <span style="color: #001080;">begin</span><span style="color: #001080;">:</span> <span style="color: #267f99;">Date</span>.<span style="color: #795e26;">now</span>() };</div><div> <span style="color: #0070c1;">asyncLocalStorage</span>.<span style="color: #795e26;">run</span>(<span style="color: #0070c1;">context</span>, <span style="color: blue;">async</span> () <span style="color: blue;">=></span> {</div><div> <span style="color: #0070c1;">logger</span>.<span style="color: #795e26;">info</span>(<span style="color: #a31515;">'Begin request'</span>);</div><div> <span style="color: #795e26;">next</span>();</div><div> });</div><div>});</div><br /><div><span style="color: #0070c1;">app</span>.<span style="color: #795e26;">get</span>(<span style="color: #a31515;">'/'</span>, <span style="color: blue;">async</span> (<span style="color: #001080;">_req</span>, <span style="color: #001080;">res</span>) <span style="color: blue;">=></span> {</div><div> <span style="color: #0070c1;">logger</span>.<span style="color: #795e26;">info</span>(<span style="color: #a31515;">'Some interesting log'</span>);</div><div> <span style="color: #001080;">res</span>.<span style="color: #795e26;">sendStatus</span>(<span style="color: #098658;">200</span>);</div><div>});</div><br /></div><div style="background-color: white; font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><span style="font-family: Times; font-size: medium; white-space: normal;">And if we make some requests now to our HTTP server we can see the expected output:</span></div><div style="background-color: white; line-height: 18px;"><span style="font-family: Menlo, Monaco, Courier New, monospace;"><span style="font-size: 12px; white-space: pre;"><br /></span></span></div><div style="background-color: white; line-height: 18px;"><span style="font-family: Menlo, Monaco, Courier New, monospace;"><span style="font-size: 12px;">info: Begin request {"begin":1666640047930,"duration":0,"traceId":"ff3fcf6c-c7e5-4a70-a3a3-143bff9b2993"}
<br />info: Some interesting log {"begin":1666640047930,"duration":1,"traceId":"ff3fcf6c-c7e5-4a70-a3a3-143bff9b2993"}<br />info: Begin request {"begin":1666640047933,"duration":0,"traceId":"4f2b4471-8d0f-481c-90bb-72bd33fe4ab2"}<br />info: Some interesting log {"begin":1666640047933,"duration":1,"traceId":"4f2b4471-8d0f-481c-90bb-72bd33fe4ab2"}<br />info: End request {"begin":1666640047930,"duration":1005,"traceId":"ff3fcf6c-c7e5-4a70-a3a3-143bff9b2993"}<br />info: End request {"begin":1666640047933,"duration":1004,"traceId":"4f2b4471-8d0f-481c-90bb-72bd33fe4ab2"}</span></span></div><div style="background-color: white; line-height: 18px;"><br /></div><div style="background-color: white; line-height: 18px;">So we have achieved what we wanted (to have a unique traceId shared for all the logs of each request) in a transparent way (using a express middleware) taking advantage of the node capabilities included in the node:async_hooks" package.</div><p>Note: You probably also want those tracing identifiers to be preserved when forwarding requests to other services but that's outside of the scope of this post.</p><p><br /></p>Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-8064773933101565542018-06-04T14:04:00.000-07:002018-06-04T14:17:55.215-07:00Video bubbles UI using Electronjs<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="margin-left: 1em; margin-right: 1em;">
</div>
<br />
<br />
After today's release of <a href="https://houseparty.com/">Houseparty's Mac app</a> showing a new approach for video conversations UI based on bubbles I was wondering if it would be possible to build a similar user experience using <a href="https://electronjs.org/">Electron framework</a> and web technologies.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://cdn.vox-cdn.com/thumbor/Senb3QEOOfmk0a4hZOLjDZ5GK78=/0x0:2880x1800/1200x0/filters:focal(0x0:2880x1800)/cdn.vox-cdn.com/uploads/chorus_asset/file/11464595/Verge.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://cdn.vox-cdn.com/thumbor/Senb3QEOOfmk0a4hZOLjDZ5GK78=/0x0:2880x1800/1200x0/filters:focal(0x0:2880x1800)/cdn.vox-cdn.com/uploads/chorus_asset/file/11464595/Verge.png" width="640" /></a></div>
<br />
It was easy to find that Electron has <a href="https://github.com/electron/electron/blob/master/docs/api/frameless-window.md#transparent-window">support for transparent and frameless windows </a>so I decided to give it a try and figure out if it would be technically possible to build something similar to that.<br />
<br />
To build the app I used the <a href="https://github.com/electron/electron-quick-start">electron quick-start</a> and only edited two files:<br />
<br />
<h2>
HTML File</h2>
<div>
First I modified the HTML file to add video capturing from the camera using the standard getUserMedia API and showed your local video stream in 3 <video> elements. To make rounded bubbles you can use standard CSS attributes:<br />
<br /></div>
<div>
<script src="https://gist.github.com/ggarber/0402c73c1367eb686fd43be3c1151510.js"></script>
</div>
<br />
<i>Note the style "webkit-app-region: drag" to make the window draggable from anywhere.</i><br />
<br />
<h2>
main.js File</h2>
<div>
You have to update the main.js file to create the main window as a frameless and transparent window:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><b>const mainWindow = new BrowserWindow({ frame: false, transparent: true });</b></span></div>
<br />
With those 2 tiny changes I was able to have something similar to the bubbles video experience created by Houseparty.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLnIZ7z_4as4Ei45gGLU7cO2CaryO0FQUSg-2nLxjnPAUyZ_St3K45hmGuBPjoQN_i6DAKhnO-H3yGJKIFY6AFIboa9SlQqTSGvcN77czyHFBUAjH-b-U5bRoEHYbbtYEVpPh66TWvOgRx/s1600/Screen+Shot+2018-06-04+at+22.29.29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="991" data-original-width="1600" height="395" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLnIZ7z_4as4Ei45gGLU7cO2CaryO0FQUSg-2nLxjnPAUyZ_St3K45hmGuBPjoQN_i6DAKhnO-H3yGJKIFY6AFIboa9SlQqTSGvcN77czyHFBUAjH-b-U5bRoEHYbbtYEVpPh66TWvOgRx/s640/Screen+Shot+2018-06-04+at+22.29.29.png" width="640" /></a></div>
<br />
<br />
So I successfully built 0.01 % of the new Houseparty Mac app using Electron! Enjoy it!<br />
<br />
<br />
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-7469650570723718822018-05-03T06:38:00.002-07:002018-05-03T06:46:02.167-07:00Two-level hashes in Redis using LUA and MsgPackRedis hashes are a very powerful data structure allowing you to store key-value properties associated with a given Redis key. For example you could store for each user all it's devices and the last time they were online:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">UserId1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> DeviceId1=1525038228</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> DeviceId2=1525038128</span><br />
<br />
But at some point maybe you need to store something more than the last time each device was online. Maybe a name, last time offline or a status (online/offline/away/busy...).<br />
<br />
So basically we want to store nested hashes with a structure similar like this one:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">UserId1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> DeviceId1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> LastOnline=1525038228</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> LastOffline=1525028228</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> Status=away</span><span style="font-family: "courier new" , "courier" , monospace;"><br /></span><span style="font-family: "courier new" , "courier" , monospace;"> DeviceId2=1525038228</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> LastOnline=1525038128</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> LastOffline=1525028128</span><br />
<div>
<span style="font-family: "courier new" , "courier" , monospace;"> Status=busy</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
</div>
<div>
<span style="font-family: inherit;">Unfortunately for us this is not a structure supported out of the box in Redis, so we need to flatten it a little bit and use something like Json values to store all that information:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">UserId1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> DeviceId1={"</span><span style="font-family: "courier new" , "courier" , monospace;">LastOnline":1525038228, "LastOffline":</span><span style="font-family: "courier new" , "courier" , monospace;">1525028228, "Status": "away"}</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> DeviceId2={"</span><span style="font-family: "courier new" , "courier" , monospace;">LastOnline":</span><span style="font-family: "courier new" , "courier" , monospace;"> 1525038128</span><span style="font-family: "courier new" , "courier" , monospace;">, "LastOffline":</span><span style="font-family: "courier new" , "courier" , monospace;"> 1525028128</span><span style="font-family: "courier new" , "courier" , monospace;">, "Status": "busy"}</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">With this approach the problem is solved, but when we need to update one of those values (f.e. LastOnline of DeviceId2) we need to do a HGET plus a HSET to redis. This is problematic because it is:</span></div>
<div>
<ul>
<li>Slower as it requires 2 round trip times to complete the operation</li>
<li>More complex as you need to use the WATCH command to run both commands simulating a transaction to avoid race conditions</li>
<li>Less efficient because you need to receive and send the whole Json value over the network</li>
</ul>
</div>
<div>
<span style="font-family: inherit;">Fortunately there are two features of Redis that combined can give us something very similar to what we need.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">The first feature is the ability to execute Lua scripts as part of a Redis command and the second feature are the standard Lua modules included in latest Redis versions allowing to serialize data as Json or MsgPack formats.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">In this python example you can see the Lua scripts to write and read any property in these nested hashes:</span><br />
<span style="font-family: inherit;"><br /></span>
<script src="https://gist.github.com/ggarber/c77a0208fd090e8e818ac9ae8ae1ffb6.js"></script>
<br />
<span style="font-weight: normal;">The first Lua script updates a nested field. To do that it gets the field value with HGET, deserialize it with '</span>cmsgpack.unpack<span style="font-weight: normal;">', then update the field, serialize it again with '</span>cmsgpack.pack<span style="font-weight: normal;">' and stores it back with HSET. </span>
<br />
<div>
<span style="font-weight: normal;">The second Lua script returns all the nested fields. To do that it gets the value with HGET, deserialize it with '</span><b>cmsgpack.unpack</b><span style="font-weight: normal;">' and converts it to a list so that it can be sent in a redis response.</span></div>
<div>
<span style="font-weight: normal;"><br /></span></div>
<div>
<span style="font-weight: normal;">Disclaimer: I haven't used Lua much in the last 10y so that can probably be simpler/cleaner.</span></div>
<br />
<h4>
Size</h4>
MessagePack serialization is more compact than Json. If you check the value stored in Redis after running the previous script you get this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">127.0.0.1:6379> hgetall user_id</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">1) "device1"</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">2) "\x81\xablast_online\xceZ\xeb\x0f\x13"</span><br />
<br />
We should make it even smaller with shorter key names (f.e. "on" instead of "LastOnline").<br />
<br />
<h4>
Performance</h4>
I didn't have time to do a detailed performance test but just to check if something was terribly wrong I tried setting and getting one of those nested values 10.000 times in a loop against a local server and checked the time it took:<br />
Option 1: Lua/MessagePack: 2.15 secs<br />
Option 2: Use raw Redis commands storing nested hash as Json and using a transaction (GET + SET) to update a subfield: 2.42 secs<br />
<br />
<br />
The same idea can be implemented with custom Redis modules instead of Lua scripts. For example that is what the <a href="http://rejson.io/">ReJSON</a> module does. That is probably a little bit faster than the Lua script approach but there are many cases where you cannot install custom Redis modules (f.e. when using managed Redis instances in AWS) </div>
<div>
<br /></div>
Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-90873271662455355132017-11-22T14:33:00.003-08:002017-11-22T14:33:54.053-08:00Playing with Redis Geo structuresOne of the things I've never used in Redis were the commands provided to store and access geolocated data. From version 3.2 Redis includes <a href="https://redis.io/commands#geo">6 new very simple commands</a> that can be used to store tags associated with coordinates and calculate distances between those tags or find the tags around some specific coordinates.<br />
<br />
Let's try to build a simple prototype to see how it works. Imagine that you have a service where users can rate anything (people, places, restaurants...)* and at some point you want to show in the UI of your app what things other people around you are rating right now. <br />
<br />
Almost every database right now has support to store this type of geographically located information but for use cases like this one where you want very fast access to ephemeral information an in-memory database can be a very good choice.<br />
<br />
<h3>
Storing data</h3>
The <a href="https://redis.io/commands/geoadd">GEOADD command</a> in Redis is the one you have to insert a new tag asociated with a specific position (latitude, longitude).<br />
<br />
The structure supported in Redis for geolocated data has an insertion and query time that is O(log(N)) complexity where N is the number of items in the set, so probably you don't want to have all the data in the same set but partition it by country or some other grouping that makes sense for your use case. In our example we could try partitioning it per city.<br />
<br />
So everytime somebody rates something identified by a tag we will do this insert in redis:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">GEOADD city latitude longitude tag</span></blockquote>
For example:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">GEOADD sanfrancisco -122 37 goldengate</span></blockquote>
Redis stores this geographical information internally in a Sorted Set, so we can use any of the sorted set commands to manipulate or retrieve the list of items stored:<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">ZRANGE sanfrancisco 0 -1<br />1) "goldengate"</span></blockquote>
<h3>
Retrieving data</h3>
There are two commands that you can use to make geographical queries on the stored data depending on your use case:<br />
<br />
<ul>
<li><a href="https://redis.io/commands/geodist">GEODIST command</a> to calculate the distance between to points.</li>
<li><a href="https://redis.io/commands/georadius">GEORADIUS command</a> to retrieve all the tags closer to a specific distance from a given point.</li>
</ul>
<br />
For our use case, everytime somebody opens the app we will retrieve all the tags around his position sorted by distance.<br />
<br />
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace;">GEORADIUS sanfrancisco -122.2 37.1 5 km<br />1) "goldengate"</span></blockquote>
<br />
<h3>
How does it work internally</h3>
<a href="https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Z-curve.svg/400px-Z-curve.svg.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="394" data-original-width="400" height="195" src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Z-curve.svg/400px-Z-curve.svg.png" width="200" /></a>As we mentioned before everything is stored inside Redis in the existing Sorted Set structures.<br />
<br />
The way those zsets are leveraged are by using a score based on the latitude and longitude. Basically by generating the zset scores interleaving the bits of the latitude and longitude of each entry you can later make queries to retrieve all the tags in a specific geographical square as a range of those scores.<br />
<br />
That way with 9 ranges you can get all the areas around a specific point. And those ranges can be of any size to be able to make queries using different radius just by trimming bits at the end of the score.<br />
<br />
This technique is called <a href="https://en.wikipedia.org/wiki/Geohash">geohashing</a> and makes this geo commands very easy to implement on top of sorted sets.<br />
<br />
Hope this is useful for other people implementing similar services, the truth is I never stop being amazed by Redis...<br />
<br />
* Disclaimer: I built that service with some friends, you can see it in http://www.pleason.com/Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-73097582330171183752017-05-08T15:36:00.003-07:002017-05-08T15:39:00.401-07:00How to (not) reuse code between Android and iOSMost of the mobile applications we build these days have to work in two different platforms (Android and iOS). Each of these platforms has its own frameworks, tools and programming languages so usually you end up building two completely separated applications and many times even built by separate teams.<br />
<br />
<i>[Note: If you are using some cross-platform development environment like react-native or Xamarin or building a Web/Hybrid app you are "lucky" and this post doesn't apply to you :)]</i><br />
<br />
Unless you are working in a very simple app at some point you will realize that there are some parts of the application that you are implementing twice because you need it in both platforms (for example some business logic or the code to make requests to the HTTP server APIs).<br />
<br />
Based on the capabilities of Android and iOS you have basically two options:<br />
<b>Option 1: Implement everything twice using the official language and libraries of each platform </b>(f.e. implement the access to HTTP APIs using Swift and URLSession in the iOS app and using Java and Volley in the Android app)<br />
<b>Option 2: Implement the reusable code in C++ </b>and compile it in the iOS app (creating a Objective C++ wrapper) and use it in the Android app (creating a JNI wrapper).<br />
<br />
These are some possible advantages of Option 1:<br />
<ul>
<li><b>Code is usually easier to read and maintain</b> when written in modern languages (for example Swift vs C++).</li>
<li><b>Native integration</b>: When using an Android library to make HTTP requests it will be probably integrated with the system proxy configuration and validates the SSL certificates with the system CAs by default.</li>
<li><b>No plumbing/boring code</b> to write to provide access to the C++ library from the application (for example with JNI). This can be partially mitigated using frameworks like <a href="http://www.swig.org/">SWIG</a> to autogenerate the wrappers but it is still boring and usually problematic.</li>
<li><b>Simpler to debug</b> because there is a single layer instead of having to make calls accross layers with different technologies(for example with JNI).</li>
<li><b>Build process faster and simpler</b> because of less libraries/tools (for example no ndk required)</li>
</ul>
These are some possible advantages of Option 2:<br />
<ul>
<li><b>No duplicated code to develop and maintain</b>.</li>
<li><b>Avoid inconsistencies</b> in naming, algorithms, protocols implementation because it is implemented in a single place.</li>
<li><b>Performance</b> can be better. Almost this is not an issue in most of the cases.</li>
</ul>
As we can see there are important pros and cons of both options so let's try another approach.... <b>Let's check what are other popular mobile libraries doing?</b><br />
<br />
I put some of those libraries in a diagram across two axis: Y for size/complexity of the library and X for number of platforms to support. Other relevant variable could be how relevant is the performance optimisation but I don't want to make a 3D diagram :) <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghVIb60LZb12PTa8WJ1ZFU-p0cXbMxETA6Rstu3f8QmcFDROAw4cVbGHH94kSspUXplNIZqP99W_fNfl46QnpMS9NwEhvpBqswExXozu7JAxlnJb-c0R7Sf6TZwk3JOojMgJnVZuz2OPnA/s1600/native_libraries.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="274" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghVIb60LZb12PTa8WJ1ZFU-p0cXbMxETA6Rstu3f8QmcFDROAw4cVbGHH94kSspUXplNIZqP99W_fNfl46QnpMS9NwEhvpBqswExXozu7JAxlnJb-c0R7Sf6TZwk3JOojMgJnVZuz2OPnA/s320/native_libraries.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">In blue libraries using Option 1 and In green libraries using Option 2</td></tr>
</tbody></table>
<i>[Apology: I picked some popular libraries I have used in the past and the lines of code and number of platforms is just an estimation, I didn't really count them]</i><br />
<br />
As we can see most of the popular libraries are using Option 1 reimplementing the library twice, once for Android and once for iOS. On the other side some big libraries related to real time communications or databases are using Option 2 implementing the core in C++ and exposing it with wrappers to Java and Objective-C applications.<br />
<h2>
Conclusion</h2>
What is the right solution probably depends on the type of project and the team building it but in my opinion in many (or most) of the cases it is less effort to develop and maintain 2 simple implementations than writing and maintaining a single more complex implementation plus the wrappers to different platforms. In addition you can (should) mitigate the issues of Option 1 making use of tools to autogenerate code when possible, for example using <a href="https://developers.google.com/protocol-buffers/">protocol buffers</a>/<a href="http://www.grpc.io/">grpc</a> for the client-server communication or <a href="http://swagger.io/">swagger</a> to generate clients for REST APIs.<br />
<br />
I'm very interested on knowing your opinion on this topic, What do you think? What are you doing right now in your projects?<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com2tag:blogger.com,1999:blog-1799786343549953072.post-70483384073684082462017-04-19T15:02:00.000-07:002017-04-19T15:10:01.254-07:00Multiplatform Travis Projects (Android, iOS, Linux in the same build)Using travis to build and test your code is usually a piece of cake and highly recommended but last week I tried to use travis for a non so conventional project and it ended up being more challenging than expected.<br />
<br />
The project was a C library with Java and Swift wrappers and my goal was to generate Android, iOS and Linux versions of that library using Travis. The main problem with my plan was that you have to define the "language" of project in your travis.yaml file and in my case... should it be android, objective-c or cpp project? <br />
<br />
It would be great if travis would support multilanguage projects [1] or multiple yaml files per project [2] but apparently none of that is going to happen in the short term.<br />
<br />
<b>Linux</b><br />
I decided to build the Linux part using docker to make sure I can use the same environment locally, in travis and in production. <br />
<br />
<b>iOS</b><br />
Given the fact that the only way to build an iOS project is using OSX images and that there is no docker support in travis for OSX I had to use the multiple operating systems capabilities in travis [3].<br />
<br />
<b>Android</b><br />
This ended up being the most challenging part. Android projects require a lot of packages (tools, sdks, ndks, gradle...) so I decided to use docker also for this to make sure I had the same environment locally and in travis. There were some docker images for this and I took many ideas form them, but I decided to generate my own [4].<br />
<br />
To not have a too crazy travis.yaml file I put all the steps to install prerequirements and to launch the build process in shell scripts (2 scripts per platform). That simplifies the travis configuration and also let me reuse the steps if I want to build locally or in jenkins eventually. My project folder looks like this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> /scripts/ios</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> before_install.sh</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> build.sh</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> /scripts/android</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> before_install.sh</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> build.sh</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> /scripts/linux</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> before_install.sh</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> build.sh</span><br />
<br />
The most interesting scripts (if any) are the android and ios ones.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> #!/bin/bash</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> echo "no additional requirements needed"</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> #!/bin/bash</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> xcodebuild build -workspace ./project.xcworkspace -scheme 'MyLibrary' -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.3'</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> #!/bin/bash</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> docker pull ggarber/android-dev</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> #!/bin/bash</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> docker run --rm -it --volume=$(pwd):/opt/workspace --workdir=/opt/workspace/samples/android ggarber/android-dev gradle build</span><br />
<br />
<br />
With that structure and those scripts the resulting travis.yaml file is very simple:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">language: cpp</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sudo: required</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">dist: xenial</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">os:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - linux</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - osx</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">osx_image: xcode8.3</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">services:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - docker</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">before_install:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then ./scripts/linux/before_install.sh ; fi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then ./scripts/android/before_install.sh ; fi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./scripts/ios/before_install.sh ; fi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">script:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then ./scripts/linux/script.sh ; fi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then ./scripts/android/script.sh ; fi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./scripts/ios/script.sh ; fi</span><br />
<br />
This is working fine although the build process is a little bit slow so these are some ideas to explore to try to improve it in the future:<br />
<ul>
<li>Linux and Android builds could run in parallel.</li>
<li>Android docker images are very big (not only mine but all the ones I found). According to docker hub it is 2GB compressed image. Probably there are ways to strip this down.</li>
<li>I'm not caching the android packages being downloaded during the build process inside the docker container.</li>
</ul>
<br />
[1] https://github.com/travis-ci/travis-ci/issues/4090<br />
[2] https://github.com/travis-ci/travis-ci/issues/3540<br />
[3] https://docs.travis-ci.com/user/multi-os/<br />
[4] https://github.com/ggarber/docker-android-dev<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-76634776914230874222017-02-06T15:21:00.002-08:002017-02-06T15:26:47.599-08:00Using Kafka as the backbone for your microservices architecture<i>Disclaimer: I only use the word microservices here to get your attention. Otherwise I would say your platform, your infrastructure or your services.</i><br />
<i><br /></i>
In many cases when your application and/or your team start growing the only way to maintain a fast development and deployment pace is to split the application and teams in different smaller units. In case of teams/people that creates some interesting and not necessarily easier to solve challenges but this post is focused on the problems and complexity created in the software/architecture part.<br />
<br />
When you split your solution in many components there are at least two problems to solve:<br />
<ul>
<li>How to pass the information from one component to another (f.e. how do you notify all the sub-components when a user signs up so that you send him notifications, start billing him, generate recommendations...)</li>
<li>How to maintain the consistency of all the partially overlapped data stored in the different components (f.e. how do you remove all the user data from all the sub-components when the user decides to drop out from your service)</li>
</ul>
<h3>
Inter component communication</h3>
<div>
At a very high level there are two communication models that are needed in most of the architectures:</div>
<div>
<ul>
<li> Synchronous request/response communications. This has his own challenges and I recommend to <a href="http://kalimotxocoding.blogspot.com/2017/01/starting-to-love-grpc-for-interprocess.html">use gRPC</a> and some best practices around load balancing, service discovery, circuit breakers.... (<a href="http://es.slideshare.net/ggarber/tef-con2016-1">find here my slides for TEFCON 2016</a>) but it is usually a well understood model.</li>
</ul>
</div>
<div>
<ul>
<li>Asynchronous event based communications where a component generates an event and one or many components receive it and implement some logic in response to that event.</li>
</ul>
</div>
<div>
The elegant way to solve this second requirement is having in the middle a bus or a queue (depending on the reliability guarantees required for the use case) where producers send events and consumers can read those events from it. There are many solutions to implement this pattern but when you have to handle heterogeneous consumers (that consume events at different rates or with different guarantees) or you have a massive amount of events or consumers the solution is not so obvious.<br />
<br /></div>
<h3>
Data consistency</h3>
<div>
The biggest problem to solve in pure microservices architectures is probably how to ensure data consistency. Once you split your application in different modules with data that is not completely independent (at the very least they all have the information about the same users) you have to figure out how to maintain that information in sync.</div>
<div>
<br /></div>
<div>
Obviously you have to try to maintain these dependencies and duplicated data as small as possible but usually at least you have to solve the problem of having the same users created in all of them.</div>
<div>
<br /></div>
<div>
To solve it you need a way to sync the data changes between different components that could be duplicated and need to be updated in other components. So basically you need a way to replicate data that ensures the eventual consistency of it.<br />
<br /></div>
<div>
<h3>
The Unified Log solution</h3>
</div>
<div>
If you look at those two problems they can be reduced to a single one: To have a real-time and reliable unified log that you can use to distribute events among different components with different needs and capabilities. That's exactly the problem that LinkedIn had and what they built Kafka to solve. <a href="https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying">The post "The Log: What every software engineer should know about real-time data's unifying abstraction" it is a very very recommended reading.</a></div>
<div>
<br /></div>
<div>
Kafka decouples the producers from the consumers including the ability to have slow consumers without affecting rest of the consumers. Kafka does that and at the same time supports very high rates of events (it is common to have hundreds of thousands per second) with very low latencies (<20 msecs easily). All these features while still being a very simple solution and providing some advanced features like organizing events in topics, preserving ordering of the events or handling consumer groups.</div>
<div>
<br /></div>
<div>
Those Kafka characteristics make it suitable to support most the <a href="https://kafka.apache.org/uses">inter-component communication use cases</a> including events distribution, logs processing and data replication/synchronization. All with a single simple solution by modeling all these communications as an infinite list of ordered events accessible for multiple consumers using a centralized unified log.</div>
<div>
<br /></div>
<div>
This post was about Kafka but all/most-of-it is equally applicable to the Amazon clone Kinesis. </div>
<div>
<br /></div>
<div>
<i>You can <a href="https://twitter.com/anarchyco">follow me in Twitter</a> if you are interested in Software and Real Time Communications.</i></div>
Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-67244553459203484682017-01-15T09:52:00.003-08:002017-01-15T11:32:56.179-08:00Starting to love gRPC for interprocess communication (1/2)In the context of a discussion around programming languages and static typing a colleague said that when you get older you stop caring about fancy technologies and you realize that is way better to just use safe and well probed solutions. <br />
<br />
I'm kind of tired of having been using loosely defined JSON-HTTP interfaces for many years and when I discovered <a href="http://www.grpc.io/">gRPC</a> last year it looked exactly what I was looking for. I would love to start using it in production as soon as possible so I decided to play with it for a while first and explain how it went.<br />
<br />
I will split my comments about gRPC in two posts. This first one about what is gRPC and what advantages provide and the next one on how to use it in our applications.<br />
<br />
gRPC embraces the <b>RPC paradigm</b> where the APIs are defined as actions receiving some arguments and replying with a response. Initially it feels like going 10y back when we started to use SOAP and similar technologies but we have to admit that is much simpler to map those primitives to our client and server code (for example no url path mapping) and it is more strict and explicit on what can and cannot be done for each operation and that usually makes the system more robust.<br />
<br />
In gRPC you define your interfaces (methods, arguments and results) in an <b>IDL using the <a href="https://github.com/google/protobuf">protocol buffers </a>format</b>. This definition is used to generate the server and client code automatically. The serialization of the calls is done using the binary protobuf format too. This makes the communication efficient and the protocol extensible being able to use all the features available in protobuf (for example composition or enum types).<br />
<br />
Two of the advantages of this approach are <b>automatic code generation and schema validation</b>. That can also be done in the "traditional" REST interfaces, but it is more tedious, less efficient and in my experience much easier to make mistakes when you add new features or refactor the code.<br />
<br />
The communication in <b>gRPC is based on HTTP2 transport</b>. This provides all the advantages of the new HTTP version (multiplexing, streaming, compression) while at the same time allows you to keep using existing HTTP infrastructure (nginx or other load balancers for example).<br />
<br />
Another special feature of gRPC is the <b>streaming support </b>that is very convenient for some APIs these days. With gRPC you are able to send a (potentially infinite) sequence of arguments to the server and receive a sequence of results from it. That is very useful to implement applications more responsive where data can be processed and displayed even if part of it is still not ready. It is also very useful for APIs based on notifications like in case of a chat application for example.<br />
<br />
When compared with other IPC frameworks like <a href="https://twitter.github.io/finagle/">Finagle</a> (disclaimer, i'm a fan of it) gRPC is still missing important features client side load balancing (<a href="https://github.com/grpc/grpc/blob/master/doc/load-balancing.md">although it is wip</a>) and some other goodies like circuit breakers, retries or service discovery. In the mean time people is implementing those features on top of the framework.<br />
<br />
The other missing piece is browsers support. Even if there is support for many languages including Javascript, the browsers limitations make it not possible to implement a gRPC compatible web client nowadays. The community is working on an <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md">extension of the protocol to support browsers</a> and in the mean time the only solution seem to be the <a href="https://github.com/grpc-ecosystem/grpc-gateway">grpc-gateway</a> proxy that generates a JSON-HTTP to gRPC proxy based on the IDL of the service with some extra annotations.<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-5068992050837617652016-11-06T14:58:00.000-08:002016-11-06T14:58:07.745-08:00Adding metrics/monitoring to the Mac menu barIn the past I used to have an extra screen close to my desk where I was able to show different dashboards with metrics to monitor the health of our services. Depending on the service we can use things like graphite, cloudwatch or google analytics.<br />
<br />
These days I'm finding some challenges to keep using that approach so I decided to explore the option of showing those metrics in the menu bar of my Mac.<br />
<br />
First thing I needed was an app allowing me to put custom stuff in the menu bar. I explored a couple of options and I ended up using BitBar:<br />
https://github.com/matryer/bitbar<br />
<br />
BitBar is a free app that is able to execute almost any script (bash, python, ruby...) and put the output of it in the menu bar with many customizable options for icons, images and format.<br />
<br />
Right now I wanted to monitor a couple of services using graphite, another from cloudwatch, show some statistics from google analytics and ideally monitor a heroku app.<br />
<br />
- Graphite: It was trivial to write a 1 line bash script curl-ing the graphite endpoint with &format=json and parsing the output with jq.<br />
https://gist.github.com/ggarber/9490390fdcb5db0251cdb6d3ca6faef9<br />
<br />
- CloudWatch: I used a python script and boto3 to be able to get CloudWatch statistics for AWS Firehose but after a couple of try-error iterations the final script was very simple too.<br />
https://gist.github.com/ggarber/8317179246ca11bfe867c93f9c6f0e2d<br />
<br />
- Google Analytics: This was the most challenging part specially because the authentication part. I ended up using this sample from Google tuned to print the exact information I wanted: https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/service-py<br />
<br />
- Heroku: I was not able to figure out how to access programmatically the requests/sec metrics that are shown in the web dashboard :(<br />
<br />
The end result is this one, you can even use emojis (:mushroom) to make the information more colorful:<br />
<span id="goog_461861501"></span><span id="goog_461861502"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid4j8LE0V2sfS11_G-STI1DZ_PSonB0Zf6BpuFQDJpoCl2snyPI6qA9LhPLJtYtpzuGwNYZk02kPCnnlvYaHv-XZUNLP4skHaTcx8uVu-Iyj_lSh66sn6GcWN_A-tQH_MPiKEDSBvn1ovm/s1600/Screen+Shot+2016-11-06+at+11.45.56+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="10" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid4j8LE0V2sfS11_G-STI1DZ_PSonB0Zf6BpuFQDJpoCl2snyPI6qA9LhPLJtYtpzuGwNYZk02kPCnnlvYaHv-XZUNLP4skHaTcx8uVu-Iyj_lSh66sn6GcWN_A-tQH_MPiKEDSBvn1ovm/s640/Screen+Shot+2016-11-06+at+11.45.56+PM.png" width="640" /></a></div>
<br />
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-6615349575731916192016-09-28T10:17:00.000-07:002016-09-28T10:21:11.114-07:00How much plumbing is required to build and deploy a server exposing the simplest HTTP APII got into an interesting discussion today about the future of development & deployment and one of the premises was that today there is too much plumbing involved on building and deploying everything.<br />
<br />
I argued that it was not that much plumbing with modern frameworks or with project templates (like yeoman ones) and that deployment had been heavily simplified in environments like Heroku.<br />
<br />
So, in this quick & dirty post I will try to prove my point building a simple HTTP server exposing a Hello-World HTTP API.<br />
<br />
<h3>
Creating the app:</h3>
<span style="font-family: "courier new" , "courier" , monospace;">➜ echo "import os</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">from flask import Flask</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">app = Flask(__name__)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">@app.route('/')</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">def hello_world():</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> return 'Hello, World!'</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span class="n" style="font-size: 0.9em;"><span style="font-size: 14.4px;">app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))</span><b style="font-size: 0.9em;">" </b></span>> app.py</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">➜ echo "flask" > requirements.txt</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">➜ echo "web: python app.py" > Procfile</span><br />
<h3>
<br />Initializing the source control (git) and comiting the changes:</h3>
<span style="font-family: "courier new" , "courier" , monospace;">➜ git init</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Initialized empty Git repository in /Users/ggb/projects/rgb/.git/</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">➜ git add *</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">➜ git commit -m "First version"</span><br />
<br />
<h3>
Deploying to production:</h3>
<span style="font-family: "courier new" , "courier" , monospace;">➜ heroku create rgb-ggb</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Creating ⬢ rgb-ggb... done</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">https://rgb-ggb.herokuapp.com/ | https://git.heroku.com/rgb-ggb.git</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">➜ git push heroku master *</span><br />
<br />
<span style="font-size: large;"><b>Try It!: https://rgb-ggb.herokuapp.com/</b></span><br />
<br />
<h2>
Summary:</h2>
<br />
Code: 7 LoC (4 is the plumbing of starting the server and it has to be done only once)<br />
Deployment: 1 file (Procfile) to tell heroku how to start your app (this is not needed for node.js apps and could be autogenerated with a yeoman template) + 1 git push command in the console (and a "heroku create" command the first time)<br />
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com2tag:blogger.com,1999:blog-1799786343549953072.post-36044558170577342532015-10-17T14:35:00.000-07:002015-10-18T10:37:20.803-07:00You need a corporate frameworkIf you are working in a big enough software development team you
probably agree that consistency in the code and development practices is
very important. Consistency is what makes you save time when joining a
new project or reviewing somebody else code, or what saves ops team
time when they deploy a new module and have to figure out how to monitor
it, or what saves analytics team time when they have to understand and
use the logs & metrics generated by a new component.<br />
<br />
To
be able to get certain degree of consistency (and quality at the same
time) it is very common these days to have coding guidelines, technical
plans, training plans, code reviews.... All those practices are very
important and help a lot to achieve certain degree of consistency, but
in my opinion they don't solve some of the most important problems and
in addition they depend a lot on human responsibility (bad, very bad,
you shouldn't trust any human).<br />
<br />
So let's try to figure out what are some of the problems we have today. Is any of these problems familiar to you?<br />
<ul>
<li>You start a new project and you don't know what folders to create
(should I create doc and test folders), how to name things (is it test
or tests, src or lib?), should I use jasmine or mocha for testing,
should I put the design of this component in a wiki page, a gdocs or a
.txt in a folder, where do I put configuration, should I mention the
third party licenses somewhere ...</li>
<li>Each component logs different things, with different names and in
different format. Do all your components log every request and
response? Do they use WARN and ERROR consistently? Do you always use
the same format for logging? I've seen teams using as many logging
libraries as components they have. The cost of not having good
consistent logging can easily make a company waste hundreds of thousands
of dollars very quickly.</li>
<li>Half of the components don't have a health or monitoring
endpoint, or if they have it the amount of information shown or the
format is totally inconsistent. One service expose the average
response time, the other the P99, the other only counters... It makes
hard (if not impossible) to monitor components so at the end nobody pays
attention to them until a customer complains.</li>
<li>My retries strategy sucks. Do you always retry when you make
requests to third party components (very common with the popularization
of "microservices" architectures)? All your components do the same
amount of retries? The timeout before retrying is always the same? Do
you retry against a different server instance?</li>
<li>The configuration of each component is different. One use XML,
the other JSON, the other env variables? In some components it can be
changed on the fly while in others it can't? In some components the
config is in git, in others in chef recipes, in others in external
configuration servers?</li>
<li>Do you have any service registration and service discovery
solution? Or some services are registered in a database, others in a
config file, others in the load balancer configuration file?</li>
</ul>
<br />
<span style="font-size: large;"><b>Use the force Luke!</b></span><br />
<br />
What you need is a corporate framework and a corporate project template.<br />
<br />
You
don't even need to create your own framework. The best example of this
kind of framework I know would be Finagle from Twitter and other teams
like Tumblr, Pinterest or Foursquare are reusing it.<br />
<br />
<a href="https://twitter.github.io/finagle/">Finagle</a>
enforces a design to build Scala services (Futures based), it provides a
TwitterServer class that automatically exposes a stats endpoint and
read configuration properties from command line arguments, includes
support for distributed logging, provides lot of clients (MySQL, HTTP,
Redis...) exposing a consistent API and automatically generating logs
and statistics, integrates with zookeeper for seamless registration and
discovery of services. If you don't know it I highly recommend you to
take a look<cite>.</cite><cite class="_Rm"></cite><br />
<br />
I
tried to implement my own framework some months ago
(https://github.com/ggarber/snap). It is very rudimentary (the maturity
level of a hackathon project) but I'm using it in production to test if
it is really helpful and even at the level of immaturity it has I found
it very helpful (I don't need to care much about consistency anymore
and it also saved me time).<br />
<br />
The other piece I think it
is mandatory is to have project template. It avoids you having to make
decisions and should have a reasonable amount of tools integrated to
automatically run tests, review styles, initiate a pull request... and
maybe even deploy.<br />
<br />
This project template can be an
Eclipse plugin, a yeoman generator or something else, but if you don't
have one I don't understand why :) As an example for node.js projects I
like this one created by a friend:
https://github.com/luma/generator-tok<br />
<br />
Hopefully I convinced
you of how important is to have a corporate framework and project
template that you use for all your components. Feedback is more than
welcomed. And contributors for the snap framework
(https://github.com/ggarber/snap) even more! :)Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-7197902430920709002015-07-04T15:05:00.001-07:002015-07-04T15:17:06.571-07:00HTTP/2 explained in 5 minutesAfter reading and playing for some days with HTTP/2 this is a summary of my understanding at a very high level.<br />
<br />
HTTP/2 is all about reducing the latency accessing web applications. It maintains the semantics (GET, POST... methods, headers, content) and url schemes of existing HTTP and it is based on the improvements proposed by Google as part of his SPDY protocol that is finally replaced by HTTP/2.<br />
<br />
The three most important changes introduced in HTTP/2 in my opinion are:<br />
1) Reduced overhead of HTTP Headers by using binary fields and header compression (HPACK).<br />
2) Ability to use a single TCP connection for multiple HTTP Requests without any type of first in line blocking (Responses can be sent in different order than requests).<br />
3) Support for pushing contents from server to client without previous request (for example the server could send some images that the browser will need when it receives the request for the HTML file referencing those images).<br />
<br />
The most controversial "feature" of HTTP/2 was making TLS mandatory. At the end the requirement was relaxed but some browsers (firefox) plan to make it mandatory anyways.<br />
<br />
Most of the relevant browsers (at least chrome and firefox and some versions of IE) already include support for HTTP 2 as well as the most popular opensource servers (nginx and Apache). So you should be able to take advantage of the new version of the protocol right now.<br />
<br />
The HTTP/2 support is negotiated using the same HTTP Upgrade mechanism used for websockets and should be transparent for users and elements in the middle (proxies),<br />
<br />
Application developers should benefit for free automatically without any change in their apps but they can get even more benefits with some extra changes:<br />
* Some tricks that are in use today like spriting or inlining resources are not needed any more. So they can simplify the build/deploy pipeline.<br />
* Server push could be automatic in some cases but in general will require developers to declare the resources to be pushed in each request. This feature requires support in the web framework being used.<br />
<br />
I made a hello world test pushing the javascript referenced in an HTML page automatically and it improved the latency as expected. I plan to repeat the test with a real page with tens/hundreds of referenced js/css/img files and publish the results.<br />
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-56528705360444326902014-12-08T20:39:00.000-08:002014-12-08T20:40:11.674-08:00Static type checking for Javascript (TypeScript vs Flow)I've never been a big fan of Javascript for large applications (nothing beyond proxies and simple services) and that is partially because in my experience the lack of static typing ends up making very easy to make mistakes and very difficult to refactor code<br />
<br />
Because of that I was very excited when I discovered TypeScript some months ago (Disclaimer: I'm not a JS expert) and I was very curious about the differences between TypeScript and Flow when some colleage pointed me to it today. So I tried to play find the seven differences, but I'm lazy and I stopped after finding one.<br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Apart from cosmetic differences and tools availability both TypeScript and Flow support type definition based on annotations, type inference and class/modules support based on EcmaScript 6 syntax. The relevant difference I found after reading/playing with them (for half an hour) is that because of the way they implement type inference Flow can detect type changes of the variables after the initial declaration making it more appropriate for legacy Javascript code where adding annotations can be not possible.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">This is some code I used to play with it with some inline comments:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">var s = "hello";<br />s.length // Both TS and Flow know that s is a string and they check they have a length method<br /><br /><br />var s: string = null;<br />s = "hello";<br />s.length // Both TS and Flow know that s is a string and they check they have a length method<br /><br />var s = null;<br />s = "hello";</span><br />
<span style="font-family: "Courier New",Courier,monospace;">s.length // TS doesn't know that this is a string but Flow knows and can check it has a length method</span>Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-30329535865056432202014-10-26T14:39:00.001-07:002014-10-26T14:40:03.683-07:00Service discovery and getting started with etcdAfter playing with some Twitter opensource components recently (mostly <a href="http://twitter.github.io/finagle/">finagle</a>) I became very interested on the concept of service discovery as a way to implement load balancing and failure recovery in the interconnection between internal services of your infrastructure. This is specially critical if you are have a <a href="http://microservices.io/">microservices architecture</a>.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSZPNYW4_JbxEoccgTGqEYfww_BmyivSraOyCzVCiTZ72u1OlNhPxW7UL8AfQPOHMNGvdk2cKtPgx5rlNN-ZCjZ4t2BwvTsgTlSYg-asmVionAKqTvFPuhs_QbuW1iFa5o3Z6sMC9BDd-o/s1600/curatorloadbalancer.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSZPNYW4_JbxEoccgTGqEYfww_BmyivSraOyCzVCiTZ72u1OlNhPxW7UL8AfQPOHMNGvdk2cKtPgx5rlNN-ZCjZ4t2BwvTsgTlSYg-asmVionAKqTvFPuhs_QbuW1iFa5o3Z6sMC9BDd-o/s1600/curatorloadbalancer.png" height="129" width="200" /></a><br />
Basically the idea of Service Discovery solutions is having a shared repository with an updated list of existing instances of type A and having mechanisms to retrieve, update and subscribe to that list allowing other components to distribute the requests to service A in an automated and reliable way.<br />
<br />
<br />
The traditional solution is <a href="http://zookeeper.apache.org/">Zookeeper</a> (based on Google Plaxos algorithm with code opensourced by Yahoo and maintained as part of the Hadoop project) but apparently other alternatives have appeared and are very promising in the near future. This <a href="http://www.activestate.com/blog/2014/05/service-discovery-solutions">post</a> summarized very well the alternatives available.<br />
<br />
One of the most interesting solutions is <a href="https://github.com/coreos/etcd">etcd</a> (simpler than Zookeeper, implemented in Go and supported by the CoreOS project). In this post I explain how to do some basic testing with it.<br />
<br />
etcd is a simple key/value store with support for expiration and watching keys that makes it ideal for service discovery. You can think of it like a redis server but distributed (with consistency and partition tolerance) and with a simple HTTP interface supporting GET, SET, DEL and LIST.<br />
<h2>
Installation</h2>
First step is to install etcd and the command line tool etcdctl.<br />
You can easily download it and install it from <a href="https://github.com/coreos/etcd/releases/">here</a> or if you are using a Mac you can just "<span style="font-family: "Courier New",Courier,monospace;">brew install etcd etcdctl</span>"<br />
<br />
<h2>
Registering a service instance</h2>
<span style="font-family: inherit;">When a new service instance in your infrastructure starts it should register himself in etcd by sending a SET request with all the information that you want to store for that instance.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><span style="font-family: inherit;">In this example we store the hostname and port of the service instance and we use a url schema like /services/SERVICE/DATACENTER/INSTANCE_ID. In addition we set a ttl of 10 seconds to make sure the information expires if it is not refreshed properly because this instance is not available.</span></span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;">var path = require('path'),<br /> uuid = require('node-uuid'),<br /> Etcd = require('node-etcd');<br /><br />var etcd = new Etcd(),</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;"> p = path.join('/', 'services', 'service_a', 'datacenter_x', uuid.v4());</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;"></span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;"></span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;">function register() {<br /> etcd.set(p,<br /> JSON.stringify({<br /> hostname: '127.0.0.1',<br /> port: '3000'<br /> }), {<br /> ttl: 60<br /> });</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;"><br /> console.log('Registered with etcd as ' + p); </span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;">}</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;">setInterval(register, 10000);</span><br />
<span style="font-family: "Courier New",Courier,monospace; font-size: x-small;">register();</span><br />
<h2>
Discovering service instances</h2>
When a service in your infrastructure requires using other service it has to send a GET request to retrieve all the available instances and subscribe (WATCH) to receive notifications of nodes down or new nodes up.<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">var path = require('path'),<br /> uuid = require('node-uuid'),<br /> Etcd = require('node-etcd');<br /><br />var etcd = new Etcd();<br />var p = path.join('/', 'services', 'service_a', 'datacenter_x');<br /><br />var instances = {};<br />function processData(data) {<br /> if (data.action == 'set') {<br /> instances[data.node.key] = data.node.value;<br /> } else if (data.action == 'expire') {<br /> delete instances[data.node.key];<br /> }<br /> console.log(instances);<br />}</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br />var watcher = etcd.watcher(p, null, {recursive: true});<br />watcher.on("change", processData);<br /><br />etcd.get(p, {recursive: true}, function(res, data) {<br /> data.node.nodes.forEach(function(node) {<br /> instances[node.key] = node.value;<br /> });<br /> console.log(instances);<br />});</span></span><br />
<br />
<h2>
Conclusions</h2>
Service discovery solutions are becoming a central place of lot of server infrastructures because of the increasing complexity in those infrastructures specially because of the raise of microservices like architectures. etcd is a ver simple approach that you can understand, deploy and start using in a less than an hour and looks more actively maintained and future proof than zookeeper. <br />
<br />
I tend to think that if Redis is able to have a good clustering solution soon it could replace specialized service discovery/configuration solutions in some cases (but I'm far from an expert in this domain).<br />
<br />
The other thing that I found missing are good frameworks making use of these technologies integrated with connection pool management, load balancing strategies, failure detection, retries... Kind of what finagle does for twitter, maybe that can be my next project :)<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-91670388450184252662014-03-20T21:54:00.001-07:002014-03-20T21:55:40.363-07:00Actor ModelThe more I write concurrent applications the more I hate it. Typically you end up having a code full of locks, queues, threads and threadpools where it is from difficult to impossible to know if it is correct or it only apparently works.<br />
<br />
Because of that I decided to do a little research on the Actor Pattern that apparently is powering frameworks like Erlang making it a very good solution for highly concurrent communication platfomrs (like Facebook Chat or WhatsApp).<br />
<br />
These are the slides I prepared, there is no much explanation on them, so feel free to ask me any question and try it! The Actor Model is fun and will simplify your life no matter if you use a framework for it or just keep in mind the concept in your future designs.<br />
<br />
<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="450" marginheight="0" marginwidth="0" scrolling="no" src="http://www.slideshare.net/slideshow/embed_code/32564721" style="max-width: 100%;" width="600"> </iframe> <br />
<div style="margin-bottom: 5px;">
<b> <a href="https://www.slideshare.net/ggarber/actor-model-pattern-for-concurrency-32564721" target="_blank" title="Actor Model pattern for concurrency">Actor Model pattern for concurrency</a> </b> from <b><a href="http://www.slideshare.net/ggarber" target="_blank">ggarber</a></b> </div>
Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com1tag:blogger.com,1999:blog-1799786343549953072.post-90533679164169570532014-02-12T20:57:00.003-08:002014-02-12T20:57:48.218-08:00Scientific way of estimating the cost of a feature in your project<br />
<div style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;">
</div>
<br />
I'm a fan of estimations as long as they are not used to try to figure out when a feature will be done. I like estimations and I think they are critical when they are used to decide which features should be done and which ones shouldn't.<br />
<br />
So, if they are so important, what is the best way to make estimations. I'm going to share my secret formula based on the things that I have read and my personal experience in my professional career (where I have to admit that my estimations are now completely different than 15 years ago).<br />
<br />
There are two key concepts that we need to understand before digging into the actual formula:<br />
<ul>
<li>One feature working doesn't mean the feature is complete or ready. Instrumentation, thread safety, unit tests, error handling, documentation, automation, unexpected problems, bug fixing... most of the times takes much more time that the implementation of the basic functionality.</li>
<li>Once you write something you usually have to maintain and not break it forever. Making sure that new features, refactors or any minor change doesn't break any existing code is a really big deal in any project with enough complexity.</li>
</ul>
<br />
Based on those key concepts we can split the cost of a feature in 3 buckets:<br />
<ul>
<li>Cost to have something working (the usual engineers initial estimation): X</li>
<li>Cost to have something ready to be shipped: Y</li>
<li>Cost to keep it working for the life of the product: Z</li>
</ul>
<span style="font-size: small;"><b>For a total cost for adding a feature to a product of X + Y + Z </b></span><br />
<br />
And now is when the scientific part is applied. Based on my experience and thousands (well, maybe 3 or 4) articles I have read I think the <a href="http://en.wikipedia.org/wiki/Pareto_principle">Pareto Principle</a> has a perfect application in this case.<br />
<br />
<span style="font-size: large;">In any project the cost of implementing the basic functionality (X) is 20% vs the 80% of implementing the rest of functionality needed to ship the product (Y). So Y = 4 * X</span><br />
<br />
<a href="https://blogger.googleusercontent.com/img/proxy/AVvXsEgEHaqDBmNswIIB_-UeqGDK15MfHc_cMsv0JX0iyHSOHQ_vGny5XKDEgBlYhhmPH0LbxnStYXqViGoDnx_kHFPY6XH47gYb1zi4QPz9W9TaSTyzf059qFonhEDp_5YYSv-4JlSC-Su1787gU1QUXz72JKOxc_y58_KulwTLSmwTxnj0HKBIkgbbySY=" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="The Circular Estimation Conjecture: You should always multiply your estimates by pi." border="0" class="aligncenter size-large wp-image-30639" src="http://www.altdevblogaday.com/wp-content/uploads/2013/11/pi-1-1024x768.jpg" height="200" style="height: 30%;" width="172" /></a><br />
I've seen a similar estimation of X + Y = PI * X that is a bit optimistic in my opinion. I recommend you to read the visual demonstration of what is called the <a href="http://www.altdevblogaday.com/2013/11/15/always-multiply-estimates-by-pi/">circular estimation conjeture</a><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<span style="font-size: large;">For the second part (the maintainability cost Z) we can apply the same Pareto Principle to get Z = 4 (X + Y)</span><br />
<br />
With all those numbers in place the conclusion is easy. The total cost of having a feature in a product is X + 4 * X + 4 * (X + 4 * X) = 25 * X<br />
<br />
<b><span style="font-size: large;">Take your initial guess (or ask any engineer) to get X, then the cost of the feature that you need to use to decide if it is worth to waste your time implementing it or not is exactly 25 * X</span></b><br />
<br />
As corollary and final demonstration of the theorem, I though this post was going to take me 5 mins to write it and it took me 25 mins and I suspect I will have to spend more than one hour discussing about it with other people.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2Fwww.altdevblogaday.com%2Fwp-content%2Fuploads%2F2013%2F11%2Fpi-1-1024x768.jpg&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://blogger.googleusercontent.com/img/proxy/AVvXsEgEHaqDBmNswIIB_-UeqGDK15MfHc_cMsv0JX0iyHSOHQ_vGny5XKDEgBlYhhmPH0LbxnStYXqViGoDnx_kHFPY6XH47gYb1zi4QPz9W9TaSTyzf059qFonhEDp_5YYSv-4JlSC-Su1787gU1QUXz72JKOxc_y58_KulwTLSmwTxnj0HKBIkgbbySY=" -->Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-70226145096660871652014-01-21T20:44:00.004-08:002014-01-23T07:00:36.773-08:00Writing sequential test scripts with nodeToday I was trying to create a node.js script to test a HTTP service but the test required multiple steps. I gave it a try by using async module to "symplify" that code and that's the ugly code I came up with.<br />
<br />
I'm not an expert in js/node, feel free to comment if I'm doing something wrong, I'm more than happy to learn.<br />
<br />
(inflightSession and create are two helper functions that I have)<br />
<br />
<span style="font-family: Arial,Helvetica,sans-serif;"><span style="font-size: large;">Test using node + jasmine: </span></span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">it("should accept valid sessionId", function(done) {<br /> async.waterfall([<br /> inflightSession,<br /><br /> function(sessionId, callback) {<br /> create({ 'sessionId':sessionId }, callback);<br /> }<br /> ], function(error, response) {<br /> expect(response.statusCode).to.equal(200);<br /> done()<br /> });<br /> });</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-family: Arial,Helvetica,sans-serif;"><span style="font-size: large;">Same test using python + unittest:</span></span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">def create_ok_test():<br /> session_id = inflight_session()<br /> response = create({ 'sessionId': session_id })<br /><br /> assert_equals(200, response.status_code)</span><br />
<span style="font-size: large;"><br /></span>
<span style="font-family: Arial,Helvetica,sans-serif;"><span style="font-size: large;">Same test using node ES6 generators (yield keyword):</span></span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">it("should accept valid sessionId", function*() {<br /> var sessionId = yield inflightSession();</span><br />
<span style="font-family: "Courier New",Courier,monospace;"> var response = yield create({ 'sessionId': sessionId });<br /> expect(response.statusCode).to.equal(200);<br /> });</span><br />
<br />
Honestly the code in python is way more readable than the existing node code, and still better even when comparing it with the new node generators. Anway definitely looks like a promising way to move forward in the node community. Some comments:<br />
<br />
ES6 generators are available under a flag in node 0.11 and are supposed to be included in 0.12.<br />
<br />
yield is a common keyword in other languages (i.e. python, C#) to exit from a function but keeping the state of that function so that you can resume the execution later.<br />
<br />
function* is the syntax to define a generator function (a function using yield inside).<br />
<br />
You need a runner supporting those generator functions (in this example jasmine needs to add support for it), basically calling the generator.next and waiting for the result (the result should be a promise or similar object) before calling generator.next again.<br />
<br />
<b>UPDATE: As I´m somehow forced to use node, I ended up creating a helper function and my tests are now like this</b><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">itx("should accept valid sessionId", </span><span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;">inflightSession, </span>function(sessionId, done) {<br /> create({ 'sessionId':sessionId }, function(error, response) {<br /> expect(response.statusCode).to.equal(200);<br /> done()<br /> });</span><br />
<span style="font-family: "Courier New",Courier,monospace;">});</span><br />
<span style="font-size: large;"><br /></span><br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com3tag:blogger.com,1999:blog-1799786343549953072.post-33304937201581366632014-01-17T09:07:00.001-08:002014-01-17T09:07:48.775-08:00Distributed Load Testing: Conclussions (5/5)Let's recap what we have done in these series and try to get some conclusions. The steps or achievements are these ones:<br />
<ol>
<li>Find and test a distributed load testing tool in python: locust.</li>
<li>Extend locust for custom (non-HTTP) protocol testing. </li>
<li>Use Instant Servers to run the locust master and slaves.</li>
<li>Implement a simple way to autostop the machines when they are not being used based on the locust logs and instant servers stop API.</li>
<li>Create a template for the slaves to be easily cloned. Use instant servers tags to define groups.</li>
<li>Fix the python Instant Server SDK and extend it with new authentication and clone features.</li>
<li>Extend locust interface adding a button to spawn machines in instant servers directly from the locust web interface.</li>
</ol>
Today I like even more python and the testing tools based in scripting instead of complex UIs. This project gave me the oportunity to discover locust and Instant Servers and highly recommend people to use them for this kind of use case, it was very easy and a lot of fun using and combining those technologies. Hopefully I can get more time for deeper integration of virtual machines in locust (with a good control UI and perhaps support for other providers).<br />
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-47461271280732411272014-01-15T00:50:00.000-08:002014-01-17T00:52:47.470-08:00Distributed Load Testing: py-smartdc and spawing slaves from locust (4/5)After all the previous work I was able to spawn slaves easily from the Instant Servers interface (just clicking Clone) or with the command line tools but I wanted to go further and explore the extension capabilities of locust to add some very simple support to the locust web page to create the slaves.<br />
<br />
<h3>
How to clone and tag an Instant Servers machine with python</h3>
I have to recognize that my first instinct was to try to create a python Instant Servers SDK, I even created the github repo and built the skeleton of the SDK, but 10 mins later somebody told me how stupid I was because there was already an official python SDK :(<br />
<br />
Ok, that's great I though, but when I tried to use it I realize that it was not compatible with Instant Servers. The "problem" is that the SDKs are maintained by joyent and even if Instant Servers is the same infrastructure, the API version is not exactly the same and the existing python SDK doesn't work with Telefonica infrastructure.<br />
<br />
The solution was easy and I created my fork and pull requested a patch [1] that still hasn't been merged. Feel free to use my fork! [2] In addition I added another patch to support username&password authentication [3], and another one to add support for cloning machines<br />
<br />
Once that's solved creating a machine by cloning the template instance we built in the previous post is very easy:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">from smartdc import DataCenter, TELEFONICA_LOCATIONS<br /><br />mad = DataCenter(location='eu-mad-1',<br /> known_locations=TELEFONICA_LOCATIONS,<br /> login='is0012', password='HNSnFAkc', api_version='6.5')<br /><br />template_found = False<br />for machine in mad.machines():<br /> tags = machine.get_tags()<br /><br /> if tags.get('locust') == 'slave-template':<br /> new_machine = machine.clone()<br /> new_machine.add_tags(locust='slave')<br /> print new_machine.name + ' ' + str(new_machine.get_tags())<br /> template_found = True<br /><br />if not template_found:<br /> print 'slave-template instance not found'</span></span><br />
<br />
<h3>
How to integrate spawning machines in locust</h3>
Locust is easily extensible in the testing scripts as we saw in the previous post but also in the UI. It is easy to add functionality to the website but modifying the template and adding more HTTP routes to process new API requests. In my case I added a new button to spawn an Instant Servers machine and a route to process that request:<br />
<br />
In locust/templates/index.html: <br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> <div class="top_box box_stop box_running" id="box_reset"><br /> <a href="/stats/reset">Reset Stats</a></br><br /><b> {% if is_distributed %}<br /> <a href="/cloud/create">Spawn Slave</a><br /> {% endif %}</b><br /> </div></span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<br />
In a new file locust/cloud.py:<br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">from smartdc import DataCenter, TELEFONICA_LOCATIONS<br />from locust import web<br /><br />mad = DataCenter(location='eu-mad-1',<br /> known_locations=TELEFONICA_LOCATIONS,<br /> login='', password='', api_version='6.5')<br /><br />@web.app.route("/cloud/create")<br />def cloud_create():<br /><br /> template_found = False<br /> for machine in mad.machines():<br /> tags = machine.get_tags()<br /><br /> if tags.get('locust') == 'slave-template':<br /> new_machine = machine.clone()<br /> new_machine.add_tags(locust='slave')<br /> return new_machine<br /><br /> return None</span></span></b><br />
<br />
You can find my locust fork in [5], don't forget to change your login and password in the cloud.py file.<br />
<br />
The result is this simple new button with the functionality required to spawn new machines automatically configured as locust slaves connected to the master for distributed testing.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgquBgkIZkvjahuFsjHJrYeoRuDeUYSrqC5YFh_BIxdqzyKZf68L6SnwHzSlHnSchIWB1yOe_l92Pwfs-vtdo0FIukSB7WlY06z_9XD5u09CR1RoEMI_1lI8SbCNtB9gxs5w32auDhWwZYI/s1600/Screen+Shot+2014-01-17+at+12.37.12+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgquBgkIZkvjahuFsjHJrYeoRuDeUYSrqC5YFh_BIxdqzyKZf68L6SnwHzSlHnSchIWB1yOe_l92Pwfs-vtdo0FIukSB7WlY06z_9XD5u09CR1RoEMI_1lI8SbCNtB9gxs5w32auDhWwZYI/s1600/Screen+Shot+2014-01-17+at+12.37.12+AM.png" height="188" width="640" /></a></div>
<br />
<br />
[1] https://github.com/atl/py-smartdc/pull/9<br />
[2] https://github.com/ggarber/py-smartdc/<br />
[3] https://github.com/atl/py-smartdc/pull/10<br />
[4] https://github.com/atl/py-smartdc/pull/11 <br />
[5] https://github.com/ggarber/locust<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-2439918820034512882014-01-03T21:22:00.000-08:002014-01-17T00:50:37.916-08:00Distributed Load Testing: Using Instant Servers for semi-automated slaves spawning (3/5)As I mention in the first post of this series virtual machines provides a very dynamic and cheap way to create slave nodes for load testing. There are different providers out there but I was interested on using <a href="http://cloud.telefonica.com/instantservers/">Instant Servers</a> (based on Joyent technology) because it is really simple to start with and it is fun to explore new solutions instead of using always the boring Amazon infrastructure.<br />
<br />
The three small features I was interested on implementing with Instant Servers were:<br />
<ul>
<li>Simplify the creation of machines preconfigured to be used of locust slaves for my load testing</li>
<li>Starting the slaves automatically on the machine startup so that the master detects them and is able to schedule jobs</li>
<li>Stopping the machine automatically when it is not used for any test for some time to make sure we don't waste our money when forgetting to stop those unused machines</li>
</ul>
<h3>
Creation of machines</h3>
To simplify the creation of machines I decided to build an instance with all the required packages and use it as template to clone the actual slave nodes. To be able to find this instance later automatically I used Instant Server tags. As far as I know unfortunately there is no UI for tagging in Instant Servers but you could use a script similar to mine [1].<br />
<br />
This machine needs to have python, locust, the test file (locustfile.py), all the basic packages (make, gcc) and the packages required to start locust automatically (see next point).<br />
<h3>
Auto starting slaves</h3>
To make sure that the slaves are started during machine startup and that we start multiple instances in every box (because locust is single threaded and I want to use multicore machines) I used supervisord with the following (hopefully autoexplicative) configuration. Tune the numprocs parameter depending on the machine you are using.<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">/etc/supervisor/conf.d/locust.conf</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><br /></span></span>
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">[program:locust]<br />command=locust -f /root/locustfile.py --master-host=81.45.23.221 --slave<br />stderr_logfile = /var/log/supervisord/locust-stderr.log<br />stdout_logfile = /var/log/supervisord/locust-stdout.log<br />process_name=%(program_name)s_%(process_num)02d<br />numprocs=4</span></span><br />
<h3>
Auto stopping machines</h3>
<br />
To monitor the usage of a machine I could be using the extensive Analytics API in instant servers but I decided to use the locust log file for simplicity. I created a python script [2] monitoring log files for activity and if there is no activity for some minutes it invokes the stop Instant Servers API to shut down that instance.<br />
<br />
Note: It was not possible to use the existing python SDK with Instant Servers and I had to fork it and made a small modification. More details in next post.<br />
<br />
<br />
PD. If I ever mention the word "cloud" in any of this posts, please feel free to insult me, I will deserve it.<br />
<br />
[1] https://gist.github.com/ggarber/8381238<br />
[2] https://gist.github.com/ggarber/8381263Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-48861573832463375642013-12-22T14:02:00.002-08:002013-12-22T14:02:18.474-08:00Scala I. New years's planOver the last years I have been trying to learn one new cool language, but because of my lack of consistency I've been jumping constantly between Erlang, Go and Scala, reading some tutorials, writting some examples... but never getting a real deep understanding of any of them.<br />
<br />
It is always good to do plans for the new year, I promise to go for running much more and to read more non technical books, so this year I really want to master one of those languages because I think it is healthy to learn new things, because it is usually a lot of fun and because I think I need it to improve the way I develop software at work.<br />
<br />
By the way, I recommend the book Seven Languages on Seven Weeks if you are trying to make a decision like mine.<br />
<br />
So, which one to choose.... (spoiler, I have decided to learn Scala).. these are my arguments...<br />
<br />
Go is very interesting but who else apart of Google is using Go for serious projects? In addition it looks like a mixture of python and C and I want something different.<br />
<br />
Erlang is really cool and probably the best language/framework for the type of applications I usually build as part of my daily job. But it is not strongly typed, there are no good tools and doc and it is very difficult to engage people on it. I tried for the last two months and I give up.<br />
<br />
Scala is slightly less cool because it has some Java inheritance, but because of that it is the easier one to introduce in a Java organization. The language is modern (support for functional programming is basic) and expressive and combined with Akka framework you are close to Erlang features.<br />
<br />
So, let's go, hopefully next post will be on some cool Scala feature.Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-36025627314436050782013-12-18T19:32:00.000-08:002014-01-17T00:50:48.069-08:00Distributed load testing: Extending locus.io for connection oriented services (2/5)I have to admit that when talking about testing scripts I'm a python fan because of the simplicity, low overhead and availability of libraries (Ruby could be a good option too). So, the first think I did when started to build my distributed load testing environment was searching google for "python distributed load testing" and as usual google didn't dissapoint me and that was the beginning of fun with<b> locust!</b><br />
<br />
<a href="http://locust.io/">locust</a> is python testing framework where you write your tests in python and the execution can be scheduled in one or multiple machines. It has facilities for HTTP testing but can be extended to other services and provides a web interface to get easy access to the progress and results of the tests.<br />
<br />
After digging for a while in the documentation (not that good) and the source code (nice code) I decided to give it a try and create my first non HTTP test. When creating a Locust class (that class defines the configuration of the tests) you can specify a client to override the default HTTP client:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">class MyLocust(Locust):</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> task_set = MyTaskSet</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> min_wait = 500</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> max_wait = 500</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> def __init__(self):</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> super(MyLocust, self).__init__()</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> self.client = MyProtocolLocustClient(host=self.host) </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /></span></span>
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">class MyProtocolLocustClient(object):<br /> def __init__(self, host):<br /> self.host = host<br /> self.connection = MyProtocolConnection(host)<br /><br /> def ping(self):<br /> request_meta = {}<br /> request_meta["start_time"] = time.time()<br /> request_meta["method"] = 'MESSAGE'<br /> request_meta["name"] = 'PING'<br /><br /> self.connection.send_message(self.connection.id(), "HI")<br /><br /> try:<br /> response = self.connection.recv_message()<br /><br /> if payload != "HI":<br /> raise Exception()<br /><br /> request_meta["response_time"] = (time.time() - request_meta["start_time"]) * 1000<br /><br /> events.request_success.fire(<br /> method=request_meta["method"],<br /> name=request_meta["name"],<br /> response_time=request_meta["response_time"],<br /> response_length=0<br /> )<br /> except Exception as e:<br /> events.request_failure.fire(<br /> method=request_meta["method"],<br /> name=request_meta["name"],<br /> response_time=0,<br /> exception=e, <br /> response=None,<br /> )</span></span><br />
<br />
<br />
That client object is available to all the locust TaskSets that you will define later with your specific tests. For example:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">class MyTaskSet(TaskSet):</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> @task</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> def my_task(l):</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"> l.client.ping()</span></span><br />
<br />
And that's all, just put that code in a locustfile.py file and run it from the command line:<br />
locust -f locustfile.py -H xxxx.yyyy.com<br />
<br />
And open a browser in http://localhost:8089 to start the test and get the results and you should get something like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxe00YNnj4NLrqSWyUKBKRkkY3EpNxq_bBngV7My2DgyMQUBjCp_TAVHis8uVl-cqfIZXQqsLjj4ezSq1P_vCU9WDVvcEfdb6wjURIqlNHoBYTd8AMCXaMht4rzxQbIZnI2nS2xK2cgYqQ/s1600/Screen+Shot+2014-01-11+at+8.15.55+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxe00YNnj4NLrqSWyUKBKRkkY3EpNxq_bBngV7My2DgyMQUBjCp_TAVHis8uVl-cqfIZXQqsLjj4ezSq1P_vCU9WDVvcEfdb6wjURIqlNHoBYTd8AMCXaMht4rzxQbIZnI2nS2xK2cgYqQ/s1600/Screen+Shot+2014-01-11+at+8.15.55+PM.png" height="180" width="640" /></a></div>
<br />
<br />Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-71747722295057947602013-12-06T00:30:00.000-08:002013-12-06T09:49:27.099-08:00Xamarin and Google Glass togetherFor the latest weeks I've been playing around with Google Glass, amazing technology!!! and for about a year I've been following Xamarin and using it for small projects/hackathons because I think that it is the PERFECT way to develop mobile apps. Today it is time to try to use both together!!!<br />
<br />
Let's start with some introductions:<br />
<br />
Google Glass runs Android 4.0.4 and you can easily run any android app [1] (thanks song for introducing me to Google Glass world). You can run any standard android app, Google just released a specific SDK (the GDK) that you can also use with Xamarin if you want deeper integration with Google Glasses.<br />
<br />
Xamarin is a cross platform framework allowing you to create mobile apps (e.g. for android) in C#/.NET will using the native UI framework in each platform.<br />
<br />
Now, let's start having fun:<br />
<br />
1. Download the standard Xamarin package and open Xamarin Studio<br />
<br />
2. Create an Android project targeting an android version <= 4.0.4<br />
<br />
3. Disable the system status bar and the application title for your Application with this code:<br />
<span style="font-family: Menlo;">
<span style="color: #444444;"> <span style="font-family: "Courier New",Courier,monospace;"> </span></span><span style="font-family: "Courier New",Courier,monospace;"><span style="color: #444444;">Window</span><span style="color: #444444;">.</span><span style="color: #444444;">AddFlags</span><span style="color: #444444;">(</span><span style="color: #3364a4;">WindowManagerFlags</span><span style="color: #444444;">.</span><span style="color: #444444;">Fullscreen</span><span style="color: #444444;">)</span><span style="color: #444444;">;</span><br />
<span style="color: #444444;"> </span><span style="color: #444444;">RequestWindowFeature</span><span style="color: #444444;">(</span><span style="color: #3364a4;">WindowFeatures</span><span style="color: #444444;">.</span><span style="color: #444444;">NoTitle</span><span style="color: #444444;">)</span><span style="color: #444444;">;</span></span></span><span style="font-family: "Courier New",Courier,monospace;">
</span><br />
<br />
4. Use standard android controls, buttons, textviews...<br />
<br />
5. Try to follow google guidelines if you want to create a coherent experience [2]<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlrtsYQkYlrQd-dNSZABxphUtohREltWyDLmU83d6klbOBkCPDi2ax-5UmHy31r-gXceH1HoyE5g5ZPf-sBSPjIL-lon_kNCYkUIopoyhWTr5IySBdkknF8iLSNiBw8qfnhP13ExueM3cy/s1600/xamarin.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlrtsYQkYlrQd-dNSZABxphUtohREltWyDLmU83d6klbOBkCPDi2ax-5UmHy31r-gXceH1HoyE5g5ZPf-sBSPjIL-lon_kNCYkUIopoyhWTr5IySBdkknF8iLSNiBw8qfnhP13ExueM3cy/s320/xamarin.png" width="240" /></a></div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="data:<;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAABAH0oBAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4nOzd+VMbSZrw8SoJ3SeHAAHiNmCDDT7afc3O7HbEbOxu7P+7sRE7sbPzznRP225f4AMw9yFAF0ISEjrfH54gI10SMtjd03T39/NDB41FHVlZpXzyycwyDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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iIiIiIjo1mAGXSIi+urwq4uIiIiu7dddL5eIiOj34fj0R4iIiIgaYfRLREREREREREREREREREREREREREREREREREREREREREREREREX4iu65csiXT5b4mIiH5PzAJNRERENyfBLaNcIiK6/Vq+9AEQERHRV4xxLxEREREREf2j4CBnIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiKi/2sPDkgAAAAABP1/3Y5ABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAm+qllJfYgJ0AAAAAASUVORK5CYII=" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img alt="" border="0" height="180" src="data:<;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAIAAABAH0oBAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4nOzd+VMbSZrw8SoJ3SeHAAHiNmCDDT7afc3O7HbEbOxu7P+7sRE7sbPzznRP225f4AMw9yFAF0ISEjrfH54gI10SMtjd03T39/NDB41FHVlZpXzyycwyDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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iIiIiIjo1mAGXSIi+urwq4uIiIiu7dddL5eIiOj34fj0R4iIiIgaYfRLREREREREREREREREREREREREREREREREREREREREREREREREX4iu65csiXT5b4mIiH5PzAJNRERENyfBLaNcIiK6/Vq+9AEQERHRV4xxLxEREREREf2j4CBnIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiKi/2sPDkgAAAAABP1/3Y5ABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAm+qllJfYgJ0AAAAAASUVORK5CYII=" style="cursor: move;" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Screen on Google Glass</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
I don't have a Xamarin license to create something much better (because of the limitations of Starter licence) but if somebody gets one for me I'll be happy to build something much better to show the powerful combination of Xamarin and Google Glass :)<br />
<br />
[1] http://songz.quora.com/How-to-run-Android-Apps-on-Google-Glass<br />
[2] https://developers.google.com/glass/design/indexGustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-21785460912223876772013-12-04T20:25:00.000-08:002014-01-17T00:50:57.423-08:00Distributed load testing: Introduction (1/5)One of the typical tasks required in all the projects when building server side components is testing and specifically load&performance testing.<br />
<br />
The first recommendation is the usage of tools or libraries as much as possible. Please, don't try to reinvent the wheel, there are plenty of libraries and tools for testing for every possible language and type of service. If your project is simple enough you can probably make use of simple command line tools like apache ab and if it gets more complex there are some tools like jmeter or soapui or frameworks for testing that should fit your needs. I have to admit my preference is not to use UI tools for testing and in general prefer simple scripts easy to hack, run and maintain. One thing to remember is that most of those tools are specially suited for HTTP services and testing is usually more complicate for other types of connection oriented protocols.<br />
<br />
The second recommendation is to use a solution supporting distributed execution of tests. That will ensure that your are able to put as much load as needed in the servers under test. Typically most of the solutions are based on a master node controlling slaves nodes where the master node schedule and submit jobs to the slaves and collect and present the results and stats received from them.<br />
<br />
In this series of posts I will explain my approach for load testing of custom non-HTTP services using python scripts and virtual machines.Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com0tag:blogger.com,1999:blog-1799786343549953072.post-60967921107526599122013-11-10T22:19:00.000-08:002013-12-05T23:31:43.321-08:00What is AOP (Aspect Oriented Programming)?For me AOP is a all about avoiding repetitive code to support common functionally by replacing that functionality with simple declarations associated to a method, class or module.<br />
<br />
Think about it for a moment, I'm sure there are a lot of common tasks that you have to repeat in your projects: logging, authentication, caching, transactions, locking... Those are commonly called cross-cutting concerns. In the worse case you will have lot of lines of code duplicated in all your methods for that functionality, in the best case you will have that code in a library and you will have only one 1 line of code in all your methods. But 1 line of code is still infinitely more than 0 lines of code!!!<br />
<br />
If we were able to define those functions in a declarative way it would be great. We need a way to tell the compiler "Add logging to this method", "All the responses in the methods of this class can be cached", "Make sure this method is executed in a transaction"... but hopefully without having to put that code in the middle of the real code of our application. Business logic of our service should be easy to read and maintain and not obscured with tons of lines implementing plumbing functionality.<br />
<br />
Most of the modern languages have a way to add some information attached to classes and methods. For example we have Annotations in Java, Attributes in C# or Decorators in Python.<br />
<br />
It is very easy to define in Java an annotation saying @Logging that can be attached to any method or class, the problem is how to associate some behavior to those annotations. This is completely language/framework dependent but taking Java as an example, we have different options:<br />
* Code modifications: A pre/post compiler task will modify the source code or byte code to introduce the code associated with each annotation.<br />
* Dynamic proxies: When using IoC the container can instantiate a proxy instead of the real object and that proxy will execute the code associated with the annotations.<br />
* Middlewares/Filters: In some frameworks (like jersey) you have the concept of filters that are executed for each request and you can add specific filters that can check the annotations in the class serving the request.<br />
<br />
// Pending (I'm lazy) adding an example<br />
<br />
I have been using AOP for years without any idea of this terminology, but just for completeness.... When we talk about AOP we usually mention Joint points and Advices. Joint points are the points where the code will be added (for example before the method or after the method) and Advices the actual code that will be added. An aspect is the combination of Joint points and Advice.Gustavo Garciahttp://www.blogger.com/profile/09231386351348682181noreply@blogger.com1