<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by K Prayogo on Medium]]></title>
        <description><![CDATA[Stories by K Prayogo on Medium]]></description>
        <link>https://medium.com/@kokizzu?source=rss-727b036791cf------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*KR6qhZNrWgE9_PE_gIXlrQ.gif</url>
            <title>Stories by K Prayogo on Medium</title>
            <link>https://medium.com/@kokizzu?source=rss-727b036791cf------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 15 Apr 2026 19:21:48 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kokizzu/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Getting started with Kubernetes]]></title>
            <link>https://kokizzu.medium.com/getting-started-with-kubernetes-d5fa47d549d4?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/d5fa47d549d4</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Fri, 28 Jan 2022 10:07:33 GMT</pubDate>
            <atom:updated>2022-02-08T06:58:54.881Z</atom:updated>
            <content:encoded><![CDATA[<p>for better reading experience (code formatting), read on the original blog link below (bottom part of this post).</p><p>Since the containerization got more popular, kubernetes gained more traction than just using single VM. In previous post I explained <a href="http://kokizzu.blogspot.com/2021/08/you-dont-need-kubernetes.html">why or when you don’t need</a> kubernetes and when you’ll need it. At least from deployment perspective we can categorize it into 5 types (based on ownership, initial cost, and granularity of the recurring cost, the need for capacity planning):</p><p>1. On-Premise Dedicated Server, our own server, our own rack, or put it the colocation, we own the hardware, and have to replace it when it’s broken, we have to be also maintaining the network part. Usually this one is best choice for internal services (software that used only by internal staff, from the security and bandwidth especially.</p><p>2. VM, we rent the “cloud” infrastructure, this can be considered IaaS (Infrastructure as a Service), we rent a virtual machine/server or sometimes named Virtual Private/Dedicated Server, so we pay monthly when the server turned on (or based on contract). Some of notable product in this category: Google Compute Engine, Amazon EC2, Azure VM, Contabo VPS/VDS, etc. Usually this one is the best for databases (unless you are using managed database service) or other stateful applications or if the number of users are limited based on the capacity planning (not whole world will be accessing this).</p><p>3. Kubernetes, we rent or use managed kubernetes, or install kubernetes on top of our own own-premise dedicated server. Usually the company will rent 3 huge servers 64 core, 256GB RAM, with very large harddisk, and let developer to deploy containers/pod inside the kubernetes themself splitted based on their team or service’s namespace. This have constant cost (those 3 huge VMs, and the managed service’s cost), some provider also provider automatic node scale out (so the kubernetes nodes/VM (where the pods will be located) can be increased based on load). Some notable product in this category: GKE, Amazon EKS, AKS, DOKS, Jelastic Kubernetes Cluster, etc.</p><p>4. Container Engine, we use the infrastructure provider’s platform, so we only need to supply container without have to rent the VM manually, some provider will deploy the container inside a single VM, some other will deploy the container on shared dedicated server/VM. Some of them Some notable product in this category: Google AppEngine, Amazon ECS/Beanstalk/Fargate, Azure App Service, Jelastic Cloud, Heroku, etc. Usually this one is the best choice for most cases on budget-wise and for scalability side.</p><p>5. Serverless/FaaS, we only need to supply the function (mostly based on a specific template) that will run on specific event (eg. on specific time like CRON, or when load balancer receiving a request like in old CGI). Usually the function put inside a container, and used as standby instance, so the scale out only happened when it receives high load. If the function requires database as dependency, it’s recommended to use managed databases that support high number of connections/connect-disconnect or offloaded to MQ/PubSub service. Notable products in this category: Google CloudRun, AWS Lambda, Azure Functions, OpenFaaS, Netlify, Vercel, Cloudflare Workers, etc. We pay this service usually based on CPU duration, number of calls, total RAM usage, bandwidth, and any other metrics, so it would be very cheap when number of function calls are small, but can be really costly if you are writting inefficient function or have large number of calls. Usually lambda only used for handling spikes or as atomic CRON.</p><p>Because of the hype, or because it fit their use cases (bunch of teams that want to do independent service deployments), and the possibility of avoiding vendor locking, sometimes a company might decide to use kubernetes. Most of the company can survive by not following the hype by only managed database (or deploying database on VM or even using docker-compose with volume binding) + container engine (for scaling out strategy), not having to train everyone to learn Kubernetes.</p><p>But today we’re gonna try one of the <a href="https://minikube.sigs.k8s.io/docs/benchmarks/imagebuild/minikubevsothers/">fastest</a> local kubernetes for development use-case (not for production).</p><p><strong>curl -LO </strong><a href="https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64"><strong>https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64</strong></a><strong><br>sudo install minikube-linux-amd64 /usr/local/bin/minikube</strong></p><p><strong>minikube start</strong></p><p><strong># use — driver=kvm2 or virtualbox if docker cannot connect internet<br>#sudo apt install virtualbox<br>#sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager<br>#sudo adduser `id -un` libvirt<br>#sudo adduser `id -un` kvm</strong></p><p><strong>alias kubectl=’minikube kubectl — ‘<br>alias k=kubectl<br></strong> <br><strong># will download kubectl if it’s the first time <br>k <br></strong> <br><strong># get pods from all namespace<br> k get po -A</strong></p><p><strong># open dashboard and authenticate <br>minikube dashboard</strong></p><p><strong># destroy minikube cluster<br>minikube ssh<br>sudo poweroff <br>minikube delete</strong></p><p>create <a href="http://kokizzu.blogspot.com/2021/08/dockerfile-template-react-express-vue.html">Dockerfile</a> you want to deploy to kubernetes cluster, or if it’s just simple single binary golang project, build locally then put it to alpine docker, then push to <a href="https://stackoverflow.com/questions/42564058/how-to-use-local-docker-images-with-minikube">image registry</a> will be work just fine:</p><p><strong># build binary<br>CGO_ENABLED=0 GOOS=linux go build -o ./bla.exe</strong></p><p><strong># create Dockerfile<br>echo ‘<br>FROM alpine:latest<br>WORKDIR /<br>COPY bla.exe .<br>CMD ./bla.exe<br>‘ &gt; Dockerfile</strong></p><p><strong># build docker image<br>VERSION=$(ruby -e ‘t = Time.now; print “v1.#{t.month+(t.year-2021)*12}%02d.#{t.hour}%02d” % [t.day, t.min]’)<br>COMMIT=$(git rev-parse — verify HEAD)<br>APPNAME=local-bla<br>docker image build -f ./Dockerfile . \<br> — build-arg “app_name=$APPNAME” \<br> -t “$APPNAME:latest” \<br> -t “$APPNAME:$COMMIT” \<br> -t “$APPNAME:$VERSION”</strong></p><p><strong># push image to minikube<br>minikube image load $APPNAME</strong></p><p><strong># create deployment config<br>echo ‘<br>apiVersion: v1<br>kind: Pod<br>metadata:<br> name: bla-pod<br>spec:<br> containers:<br> — name: bla<br> image: bla <br> imagePullPolicy: Never <br> env:<br> — name: BLA_ENV<br> value: “ENV_VALUE_TO_INJECT”<br> # if you need access to docker-compose outside the kube cluster<br> # use minikube ssh, route -n, check the ip of the gateway<br> # and use that ip as connection string<br> # it should work, as long the port forwarded<br> restartPolicy: Never<br>‘ &gt; bla-pod.yaml</strong></p><p><strong># deploy<br>kubectl apply -f bla-pod.yaml</strong></p><p><strong># check<br>k get pods <br>k logs bla-pod</strong></p><p><strong># delete deployment<br>kubectl delete pod bla-pod</strong></p><p>If you need NewRelic log forwarding, it’s just as easy as adding a helm chart (it would automatically attach new pod logs and send it to newrelic):</p><p><strong>curl </strong><a href="https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3"><strong>https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3</strong></a><strong> | bash<br>helm repo add newrelic </strong><a href="https://helm-charts.newrelic.com"><strong>https://helm-charts.newrelic.com</strong></a><strong> <br>helm search repo newrelic/<br>helm install newrelic-logging newrelic/newrelic-logging — set licenseKey=eu01xx2xxxxxxxxxxxxxxxRAL<br>kubectl get daemonset -o wide -w — namespace default newrelic-logging</strong></p><p>The next step should be adding load balancer or <a href="https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/">ingress</a> so that pod can receive http requests.</p><p><em>Originally published at </em><a href="https://kokizzu.blogspot.com/2022/01/getting-started-with-kubernetes.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d5fa47d549d4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Easy minimal Ubuntu VM on any OS]]></title>
            <link>https://kokizzu.medium.com/easy-minimal-ubuntu-vm-on-any-os-70aac2ce470b?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/70aac2ce470b</guid>
            <category><![CDATA[virtualization]]></category>
            <category><![CDATA[ubuntu]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Fri, 21 Jan 2022 08:11:35 GMT</pubDate>
            <atom:updated>2022-02-01T05:57:33.312Z</atom:updated>
            <content:encoded><![CDATA[<p>for better reading experience (code formatting), read on the original blog, link on bottom section of this post.</p><p>Normally we use LXC/LXD, KVM, QEMU, Docker, Vagrant, VirtualBox, VMWare or any other virtualization and containerization software to spawn a VM-like instance locally. Today we’re gonna try <a href="https://multipass.run/">multipass</a>, a tool to spawn and orchestrate ubuntu VM. To install multipass, it’s as easy as running these commands:</p><blockquote><strong>snap install multipass<br>ls -al /var/snap/multipass/common/multipass_socket<br>snap info multipass</strong></blockquote><p>To spawn a VM on Ubuntu (for other OSes, see the link above), we can run:</p><blockquote><strong>multipass find</strong></blockquote><blockquote><strong>Image Aliases Version Description<br>snapcraft:core18 18.04 20201111 Snapcraft builder for Core 18<br>snapcraft:core20 20.04 20210921 Snapcraft builder for Core 20<br>snapcraft:core 16.04 20210929 Snapcraft builder for Core 16<br>core core16 20200818 Ubuntu Core 16<br>core18 20211124 Ubuntu Core 18<br>18.04 bionic 20220104 Ubuntu 18.04 LTS<br>20.04 focal,lts 20220118 Ubuntu 20.04 LTS<br>21.10 impish 20220118 Ubuntu 21.10<br>daily:22.04 devel,jammy 20220114 Ubuntu 22.04 LTS<br>appliance:adguard-home 20200812 Ubuntu AdGuard Home Appliance<br>appliance:mosquitto 20200812 Ubuntu Mosquitto Appliance<br>appliance:nextcloud 20200812 Ubuntu Nextcloud Appliance<br>appliance:openhab 20200812 Ubuntu openHAB Home Appliance<br>appliance:plexmediaserver 20200812 Ubuntu Plex Media Server Appliance<br>anbox-cloud-appliance latest Anbox Cloud Appliance<br>charm-dev latest A development and testing environment for charmers<br>minikube latest minikube is local Kubernetes</strong></blockquote><blockquote><strong>multipass launch — name groovy-lagomorph # 20.04</strong></blockquote><blockquote><strong>multipass list<br>Name State IPv4 Image<br>groovy-lagomorph Running 10.204.28.99 Ubuntu 20.04 LTS</strong></blockquote><blockquote><strong>multipass info — all<br>Name: groovy-lagomorph<br>State: Running<br>IPv4: 10.204.28.99<br>Release: Ubuntu 20.04.3 LTS<br>Image hash: e1264d4cca6c (Ubuntu 20.04 LTS)<br>Load: 0.00 0.00 0.00<br>Disk usage: 1.3G out of 4.7G<br>Memory usage: 134.2M out of 976.8M<br>Mounts: —</strong></blockquote><p>To run shell inside newly spawned VM, we can run:</p><blockquote><strong>multipass shell groovy-lagomorph</strong></blockquote><blockquote><strong>multipass exec groovy-lagomorph — bash</strong></blockquote><p>If you need to simulate ssh, according to this <a href="https://github.com/canonical/multipass/issues/913">issue</a> you can either:</p><blockquote><strong>sudo ssh -i /var/snap/multipass/common/data/multipassd/ssh-keys/id_rsa ubuntu@10.204.28.99</strong></blockquote><blockquote><strong># or add ssh key before launch on cloud-init.yaml<br>ssh_authorized_keys:<br> — &lt;your_ssh_key&gt;</strong></blockquote><blockquote><strong># or copy ssh key manually after launch<br>sudo ssh-copy-id -f -o ‘IdentityFile=/var/snap/multipass/common/data/multipassd/ssh-keys/id_rsa’ -i ~/.ssh/id_rsa.pub ubuntu@10.204.28.99</strong></blockquote><p>If to stop/start/delete the VM:</p><blockquote><strong>multipass stop groovy-lagomorph<br>multipass start groovy-lagomorph<br>multipass delete groovy-lagomorph<br>multipass purge</strong></blockquote><p>What technology used by multipass? it’s QEMU, but maybe it’s different on another platform (it can run on Windows and MacOSX too).</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2022/01/easy-minimal-ubuntu-vm.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=70aac2ce470b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Huge List of Database Benchmark (2019)]]></title>
            <link>https://kokizzu.medium.com/huge-list-of-database-benchmark-2019-73c2225cb635?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/73c2225cb635</guid>
            <category><![CDATA[benchmark]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[mysql]]></category>
            <category><![CDATA[singlestore]]></category>
            <category><![CDATA[scylladb]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:36:35 GMT</pubDate>
            <atom:updated>2022-01-05T06:36:35.644Z</atom:updated>
            <content:encoded><![CDATA[<p>Today we will benchmark a single node version of distributed database (and some non-distributed database for comparison), the client all written with Go (with any available driver). The judgement will be about performance (that mostly write, and some infrequent read), not about the distribution performance (I will take a look in some other time). I searched a lot of database from <a href="https://db-engines.com/">DbEngines</a> for database that could suit my needs for my next project. For session kv-store I’ll be using obviously first choice is <a href="http://aerospike.com/">Aerospike</a>, but since they <a href="https://discuss.aerospike.com/t/failed-assertion-hardware-hardware-c-625-error-while-reading-list-of-online-cpus/5859/2">cannot be run</a> inside server that I rent (that uses <a href="https://openvz.org/">OpenVZ</a>), so I’ll go for second choice that is <a href="http://redis.io/">Redis</a>. Here’s the list of today’s contender:</p><ul><li><a href="https://crate.io/">CrateDB</a>, a highly optimized for huge amount of data (they said), probably would be the best for updatable time series, also with built-in search engine, so this one is quite fit my use case probably to replace [<a href="https://github.com/go-ego/riot">Riot</a> (small scale) or <a href="https://manticoresearch.com/">Manticore </a>(large scale)] and [<a href="https://www.influxdata.com/">InfluxDB </a>or <a href="https://www.timescale.com/">TimescaleDB</a>], does not support auto increment</li><li><a href="https://www.cockroachlabs.com/">CockroachDB</a>, self-healing database with PostgreSQL-compatible connector, the community edition does not support table partitioning</li><li><a href="https://www.memsql.com/">MemSQL</a>, which also can replace kv-store, there’s a limit of 128GB RAM for free version. Row-store tables can only have one PRIMARY key or one UNIQUE key or one AUTO increment column that must be a SHARD key, and it cannot be updated or altered. Column-store tables does not support UNIQUE/PRIMARY key, only SHARD KEY. The client/connector is MySQL-compatible</li><li><a href="http://mariadb.org/">MariaDB</a> (MySQL), one of the most popular open source RDBMS, for the sake of comparison</li><li><a href="https://www.postgresql.org/">PostgreSQL</a>, my favorite RDBMS, for the sake of comparison</li><li><a href="https://www.nuodb.com/dev-center/community-edition-download">NuoDB</a> on <a href="https://www.nuodb.com/techblog/benchmarking-google-cloud-spanner-cockroachdb-nuodb">another benchmark</a> even faster than GoogleSpanner or CockroachDB, the community edition only support 3 transaction engine (TE) and 1 storage manager (SM)</li><li><a href="http://yugabyte.com/">YugaByteDB</a>, distributed KV+SQL with Cassandra and PostgreSQL compatible protocol. Some of SQL syntax not yet supported (ALTER USER, UNIQUE on CREATE TABLE).</li><li><a href="http://scylladb.com/">ScyllaDB</a>, a C++ version of Cassandra. All Cassandra-like databases has a lot of restrictions/annoyances by design compared to traditional RDBMS (cannot CREATE INDEX on composite PRIMARY KEY, no AUTO INCREMENT, doesn’t support UNION ALL or OR operator, must use COUNTER type if you want to UPDATE x=x+n, cannot mix COUNTER type with non-counter type on the same table, etc), does not support ORDER BY other than clustering key, does not support OFFSET on LIMIT.</li><li><a href="https://clickhouse.yandex/">Clickhouse</a>, claimed to be fastest and one of the most storage space efficient OLAP database, but doesn’t support UPDATE/DELETE-syntax (requires ALTER TABLE to UPDATE/DELETE), only support batch insert, does not support UNIQUE, AUTO INCREMENT. Since this is not designed to be an OLTP database, obviously this benchmark would be totally unfair for Clickhouse.</li></ul><p>What’s the extra motivation of this post?</p><p>I almost never use distributed database, since all of my project have no more than 200 concurrent users/sec. I’ve encountered bottleneck before, and the culprit is multiple slow complex queries, that could be solved by queuing to another message queue, and process them one by one instead of bombing database’s process at the same time and hogging out the memory.</p><p>The benchmark scenario would be like this:<br> 1. 50k inserts of single column string value, 200k inserts of 2 column unique value, 900k insert of unique<br><strong>INSERT INTO users(id, uniq_str) — x50k</strong> <br><strong>INSERT INTO items(fk_id, typ, amount) — x50k x4</strong> <br><strong>INSERT INTO rels(fk_low, fk_high, bond) — x900k</strong></p><p>2. while inserting at 5%+, there would be at least 100k random search queries of unique value/, and 300k random search queries, every search queries, there would be 3 random update of amount<br><strong>SELECT * FROM users WHERE uniq_str = ? — x100k</strong> <br><strong>SELECT * FROM items WHERE fk_id = ? AND typ IN (?) — x100k x3</strong> <br><strong>UPDATE items SET amount = amount + xxx WHERE id = ? — x100k x3</strong></p><p>3. while inserting at 5%+, there would be also at least 100k random search queries<br><strong>SELECT * FROM items WHERE fk_id = ?</strong> <br> 4. while inserting at 5%+, there also at least 200k query of relations and 50% chance to update the bond<br><strong>SELECT * FROM rels WHERE fk_low = ? or fk_high = ? — x200k<br>UPDATE rels SET bond = bond + xxx WHERE id = ? — x200k / 2</strong></p><p>This benchmark represent simplified real use case of the game I’m currently develop. Let’s start with <strong>PostgreSQL 10.7</strong> (current one on Ubuntu 18.04.1 LTS), the <a href="https://pgtune.leopard.in.ua/">configuration</a> generated by pgtune website:</p><p>For slow databases, all values reduced by 20 except query-only.</p><p><strong>[Pg] RandomSearchItems (100000, 100%) took 24.62s (246.21 µs/op)</strong></p><p><strong>innodb_buffer_pool_size=4G</strong></p><p><strong>CREATE USER ‘b1’@’localhost’ IDENTIFIED BY ‘b1’;</strong></p><p><strong>GRANT ALL PRIVILEGES ON b1.* TO ‘b1’@’localhost’;</strong></p><p><strong>sudo mysqltuner # not sure if this useful</strong></p><p><strong>[My] RandomSearchItems (100000, 100%) took 16.62s (166.20 µs/op)</strong> <br><strong>[My] SearchRelsAddBonds (10000, 100%) took 86.32s (8631.74 µs/op)</strong><br><strong>[My] UpdateItemsAmounts (5000, 100%) took 172.35s (34470.72 µs/op)</strong><br> [My] InsertUsersItems (2500, 100%) took 228.52s (91408.86 µs/op)<br><strong>USERS CR : 2500 / 4994 </strong><br><strong>ITEMS CRU : 17500 / 14982 + 696542 / 13485 </strong><br><strong>RELS CRU : 2375 / 12871 / 6435 </strong><br><strong>SLOW FACTOR : 20 </strong><br><strong>CRU µs/rec : 10213.28 / 23.86 / 13097.44</strong></p><p>Next we’ll try with <strong>MemSQL 6.7.16–55671ba478</strong>, while the insert and update performance is amazing, the query/read performance is 3–4x slower than traditional RDBMS:</p><p><strong>$ memsql-admin start-node — all</strong></p><p><strong>$ go run memsql.go lib.go # 4 sec before start RU</strong></p><p><strong>$ go run memsql.go lib.go # SLOW FACTOR 5</strong></p><p><strong>[Crate] SearchRelsAddBonds (10000, 100%) took 49.11s (4911.38 µs/op)</strong> <br><strong>[Crate] RandomSearchItems (100000, 100%) took 101.40s (1013.95 µs/op)</strong><br> [Crate] UpdateItemsAmounts (5000, 100%) took 246.42s (49283.84 µs/op)<br><strong>[Crate] InsertUsersItems (2500, 100%) took 306.12s (122449.00 µs/op)</strong><br> USERS CR : 2500 / 4965<br> ITEMS CRU : 17500 / 14894 + 690161 / 14895<br> RELS CRU : 2375 / 4336 / 2168<br> SLOW FACTOR : 20<br> CRU µs/rec : 13681.45 / 146.92 / 19598.85</p><p>Next is <strong>CockroachDB 19.1</strong>, the result:</p><p><strong>$ go run cockroach.go lib.go</strong> <br><strong>[Cockroach] SearchRelsAddBonds (10000, 100%) took 59.25s (5925.42 µs/op)</strong> <br><strong>[Cockroach] RandomSearchItems (100000, 100%) took 85.84s (858.45 µs/op)</strong><br><strong>[Cockroach] UpdateItemsAmounts (5000, 100%) took 261.43s (52285.65 µs/op</strong><br> [Cockroach] InsertUsersItems (2500, 100%) took 424.66s (169864.55 µs/op)<br> USERS CR : 2500 / 4988<br> ITEMS CRU : 17500 / 14964 + 699331 / 14964<br> RELS CRU : 2375 / 5761 / 2880<br> SLOW FACTOR : 20<br> CRU µs/rec : 18979.28 / 122.75 / 19022.43</p><p>Next is <strong>NuoDB 3.4.1</strong>, the storage manager and transaction engine <a href="http://www.nuodb.com/techblog/scale-out-new-nuodb-community-edition-release">config</a> and the benchmark result:</p><p><strong>$ chown nuodb:nuodb /media/nuodb</strong> <br><strong>$ nuodbmgr — broker localhost — password nuodb1pass</strong> <br><strong> start process sm archive /media/nuodb host localhost database b1 initialize true </strong><br><strong> start process te host localhost database b1 </strong><br><strong> — dba-user b2 — dba-password b3<br>$ nuosql b1 — user b2 — password b3</strong> <br><strong>$ go run nuodb.go lib.go</strong> <br><strong>[Nuo] RandomSearchItems (100000, 100%) took 33.79s (337.90 µs/op)</strong><br><strong>[Nuo] SearchRelsAddBonds (10000, 100%) took 72.18s (7218.04 µs/op)</strong><br><strong>[Nuo] UpdateItemsAmounts (5000, 100%) took 117.22s (23443.65 µs/op)</strong><br><strong>[Nuo] InsertUsersItems (2500, 100%) took 144.51s (57804.21 µs/op)</strong><br><strong>USERS CR : 2500 / 4995 </strong><br><strong>ITEMS CRU : 17500 / 14985 + 698313 / 14985 </strong><br><strong>RELS CRU : 2375 / 15822 / 7911 </strong><br><strong>SLOW FACTOR : 20 </strong><br><strong>CRU µs/rec : 6458.57 / 48.39 / 8473.22</strong></p><p>Next is <strong>YugaByte 1.2.5.0</strong>, the result:</p><p><strong>export YB_PG_FALLBACK_SYSTEM_USER_NAME=user1</strong><br><strong>./bin/yb-ctl — data_dir=/media/yuga create</strong> <br><strong># edit yb-ctl set use_cassandra_authentication = True</strong><br><strong>./bin/yb-ctl — data_dir=/media/yuga start</strong></p><p><strong>./bin/cqlsh -u cassandra -p cassandra</strong></p><p>When INSERT is not batched on <strong>Clickhouse 19.7.3.9</strong>:</p><p>$ go run clickhouse-1insertPreTransaction.go lib.go <br> [Click] InsertUsersItems (2500, 100%) took 110.78s (44312.56 µs/op)<br> [Click] RandomSearchItems (100000, 100%) took 306.10s (3060.95 µs/op)<br> [Click] SearchRelsAddBonds (10000, 100%) took 534.91s (53491.35 µs/op)<br> [Click] UpdateItemsAmounts (5000, 100%) took 710.39s (142078.55 µs/op)<br> USERS CR : 2500 / 4999<br> ITEMS CRU : 17500 / 14997 + 699615 / 15000<br> RELS CRU : 2375 / 18811 / 9405<br> SLOW FACTOR : 20<br> CRU µs/rec : 4951.12 / 437.52 / 52117.48</p><p>These benchmark performed using i7–4720HQ 32GB RAM with SSD disk. At first there’s a lot that I want to add to this benchmark (maybe someday) to make this huge ‘__’), such as:</p><ul><li><a href="https://dgraph.io/">DGraph</a>, a graph database written in Go, the backup is local (same as MemSQL, so you cannot do something like this ssh foo@bar “pg_dump | xz — -c” | pv -r -b &gt; /tmp/backup_`date +%Y%m%d_%H%M%S`.sql.xz”)</li><li><a href="http://cayley.io/">Cayley</a>, a graph layer written in Go, can support many backend storage</li><li><a href="https://www.arangodb.com/">ArangoDB</a>, multi-model database, with built-in Foxx Framework for creating REST APIs, has unfamiliar AQL syntax</li><li><a href="https://www.mongodb.com/">MongoDB</a>, one of the most popular open source document database, for the sake of comparison, I’m not prefer this one because of the memory usage.</li><li><a href="http://influxdb.com/">InfluxDB</a> or <a href="http://timescale.com/">TimeScaleDB</a> or <a href="https://siridb.net/">SiriDB</a> or <a href="https://griddb.net/en/">GridDB</a> for <a href="https://docs.google.com/spreadsheets/d/1sMQe9oOKhMhIVw9WmuCEWdPtAoccJ4a-IuZv4fXDHxM/edit#gid=0">comparison</a> with Clickhouse</li><li><a href="http://redis.io/">Redis</a> or <a href="https://www.researchgate.net/publication/329177796_Performance_Benchmarking_of_Key-Value_Store_NoSQL_Databases">SSDB</a> or <a href="http://ledisdb.com/">LedisDB</a> or <a href="https://github.com/CodisLabs/codis">Codis</a> or <a href="https://github.com/buraksezer/olric">Olric</a> or <a href="https://github.com/tidwall/summitdb">SummitDB</a>, obviously for the sake of comparison. Also <a href="https://github.com/mosuka/cete">Cete</a>, distributed key-value but instead using memcache protocol this one uses gRPC and REST</li><li><a href="https://www.tarantool.io/">Tarantool</a>, a redis competitor with ArrangoDB-like features but with Lua instead of JS, I want to see if this simpler to use but with near equal performance as Aerospike</li><li><a href="http://aerospike.com/">Aerospike</a>, fastest distributed kv-store I ever test, just for the sake of comparison, the free version limited to 2 namespace with 4 billions object. Too bad this one <a href="https://discuss.aerospike.com/t/failed-assertion-hardware-hardware-c-625-error-while-reading-list-of-online-cpus/5859/3">cannot be started</a> on OpenVZ-based VM.</li><li><a href="https://www.couchbase.com/products/editions">Couchbase</a>, document oriented database that support SQL-like syntax (N1QL), the free for production one is few months behind the enterprise edition. Community edition cannot create index (always error 5000?).</li><li><a href="https://griddb.net/">GridDB</a>, in-memory database from Toshiba, benchmarked to be superior to Cassandra</li><li><a href="https://mariadb.com/products/mariadb-platform/components/#xpand">ClustrixDB</a> (New name: MariaDB XPand), distributed columnstore version of MariaDB, community version does not support automatic failover and non-blocking backup</li><li><a href="https://altibase.com/">Altibase</a>, open source in-memory database promoted to be Oracle-compatible, not sure what’s the limitation of the open source version.</li><li><a href="https://redislabs.com/redis-enterprise/redis-graph/">RedisGraph</a>, <a href="https://redislabs.com/blog/new-redisgraph-1-0-achieves-600x-faster-performance-graph-databases/">fastest</a> in-memory graph database, community edition available.</li><li><a href="http://rethinkdb.com/">RethinkDB</a>, document-oriented database, last ubuntu package cannot be installed, probably because the project no longer maintained</li><li><a href="http://orientdb.org/">OrientDB</a>, multi model (document and graph database), their screenshot looks interesting, multi-model graph database, but too bad both Golang driver are unmaintained and probably unusable for latest version (3.x)</li><li><a href="https://pingcap.com/en/">TiDB</a>, a work in progress approach of CockroachDB but with MySQL-compatible connector, as seen from benchmark above, there’s a lot of errors happening</li><li><a href="https://github.com/rqlite/rqlite">RQLite</a>, a distributed SQLite, the go driver by default not threadsafe</li><li><a href="https://www.voltdb.com/">VoltDB</a>, seems not free, since the website shows “free evaluation”</li><li><a href="https://dbdb.io/db/hyperdex">HyperDex</a>, have good benchmark on paper, but no longer maintained</li><li><a href="https://github.com/LMDB/memcachedb">LMDB-memcachedb</a>, faster version of memcachedb, a distributed kv, but no longer maintained</li><li><a href="https://github.com/apple/foundationdb">FoundationDB</a>, a multi-model database, built from kv-database with additional layers for other models, seems to have complicated APIs</li><li><a href="https://www.tigergraph.com/product/">TigerGraph</a>, <a href="https://www.tigergraph.com/benchmark/">fastest</a> distributed graph database, developer edition free but may not be used for production</li></ul><p>The chart (lower is better) shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Crp5GMKOr4OKxEttnIsmiA.png" /></figure><p>Other 2018’s benchmark <a href="https://medium.com/yugabyte/go-jeks-performance-benchmarking-of-cockroachdb-tidb-yugabyte-db-on-kubernetes-9fde0127b00">here</a> (tl;dr: CockroachDB mostly higher throughput, YugabyteDB lowest latency, TiDB lowest performance among those 3).</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2019/04/huge-list-of-database-benchmark.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73c2225cb635" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cleanup git and docker Disk Usage]]></title>
            <link>https://kokizzu.medium.com/cleanup-git-and-docker-disk-usage-d31497cbf480?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/d31497cbf480</guid>
            <category><![CDATA[git]]></category>
            <category><![CDATA[disk-usage]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:32:04 GMT</pubDate>
            <atom:updated>2022-01-05T06:32:04.063Z</atom:updated>
            <content:encoded><![CDATA[<p>Sometimes our cloned repository became so freakin large, for example golang’s google.golang.org/api, currently mine consume about 1.1GB. We could compress it using garbage collection parameter:</p><p><strong># 664 MB in about 20 seconds</strong></p><p>Or if you have time you can use aggresive GC, like this:</p><p><strong># 217 MB in about 5 minutes</strong></p><p><strong>mv ../temp/{shallow,objects} .git</strong></p><p><strong># 150 MB in about 2 seconds</strong></p><p>Next you can reclaim space from docker using this command:</p><p>For more disk usage analysis you can use <a href="http://www.marzocca.net/linux/baobab/">baobab</a> for linux or <a href="https://windirstat.net/">windirstat</a> on windows.</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2020/10/cleanup-git-and-docker-disk-usage.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d31497cbf480" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Golang Serialization Benchmark 2020 Edition]]></title>
            <link>https://kokizzu.medium.com/golang-serialization-benchmark-2020-edition-441662b335c1?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/441662b335c1</guid>
            <category><![CDATA[go]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[protobuf]]></category>
            <category><![CDATA[benchmark]]></category>
            <category><![CDATA[serialization]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:30:26 GMT</pubDate>
            <atom:updated>2022-01-05T06:30:26.812Z</atom:updated>
            <content:encoded><![CDATA[<p>These benchmark results taken from <a href="https://github.com/alecthomas/go_serialization_benchmarks">electhomas’ repo</a>, have a quite interesting result (new serialization formats). Let’s see how much serialization cost, the fastest:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/835/1*Ca38Oa-dd_juQKzxylx6Uw.png" /></figure><p>As we can see, Mum, Gencode, Colfer, Bebop, Gotiny, XDR2, MsgPack wins in terms of performace, in cost of serialization size. Let’s check the deserialization performace, the fastest are:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/833/1*2Xe7a13V3BmCasiqzCspPw.png" /></figure><p>In this part, Bebop, XDR2, Gencode, Colfer, Mum, Gogoprotobuf, Gotiny, FlatBuffers, MsgPack wins this part. So if your bandwidth is unlimited, you can choose these format as your serialization format. You can access the reformatted spreadsheet <a href="https://docs.google.com/spreadsheets/d/1rgpcoTR5ECXJXKyJz9igUpHzSoVGjBhl2I4d5NFTgGI/edit#gid=1219979737">here</a>. Here’s combined result of serialization and deserialization and addition of serialization size and allocation needed to deserialize.</p><p>Here are the links for high performing libraries used:</p><ul><li><a href="https://github.com/200sc/bebop">Bebop</a> codegen from .bop (need to create schema like protobuf)</li><li><a href="https://github.com/andyleap/gencode">Gencode</a> codegen from .schema (similar to go syntax)</li><li><a href="http://kokizzu.blogspot.com/2020/12/github.com/calmh/xdr">XDR2</a> codegen version (other libs are using reflection/automatic), unmaintained</li><li><a href="http://github.com/itsmontoya/mum">Mum</a> manual (must create the method to serialize and deserialize yourself), new name is <a href="https://github.com/mojura/enkodo">enkodo</a></li><li><a href="http://github.com/pascaldekloe/colfer">Colfer</a> codegen from .colf (similar to go syntax)</li><li><a href="https://github.com/gogo/protobuf">Gogoprotobuf</a> codegen from .proto</li><li><a href="http://github.com/niubaoshu/gotiny">Gotiny</a> codegen, not recommended for production</li><li><a href="https://github.com/tinylib/msgp">MsgPack</a> codegen version (other libs are using reflection/automatic), from .go source file using <a href="https://blog.golang.org/generate">go:generate</a>, supported in lots of language</li><li><a href="http://github.com/google/flatbuffers">FlatBuffers</a> codegen, can be used in gRPC</li></ul><p>What are the difference between codegen, automatic, manual?</p><ul><li><strong>codegen</strong> means there are step to generate golang function by writing a schema definition file then run a program to convert that file to specific programming language implementation, sometimes using other format (so you cannot add custom tag to generated .go file), sometimes using golang struct with tag (like codegen version of <a href="https://msgpack.org/index.html">msgpack</a> above you need to label each property with , so you can add your own custom tag on the struct property, eg. <strong>`json:”bar,omitempty” form:”baz” bson:”blabla”` </strong>)</li><li><strong>manual</strong> means you must write the serialization and deserialization yourself for each property of the struct (the library only the helper), this allows highly flexible system, so for example you read 1 byte first, then if the value is 1 then you read a string, if 2 you read int32, and so on. This can also be useful to parse network packet if having the same endian.</li><li><strong>automatic</strong> means you don’t need to write any schema, it uses <a href="https://blog.golang.org/laws-of-reflection">reflection</a> so should be slower than codegen version.</li></ul><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2020/12/golang-serialization-benchmark-2020.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=441662b335c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GOPS: Trace your Golang service with ease]]></title>
            <link>https://kokizzu.medium.com/gops-trace-your-golang-service-with-ease-3b7e2a62f24b?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b7e2a62f24b</guid>
            <category><![CDATA[go]]></category>
            <category><![CDATA[profiling]]></category>
            <category><![CDATA[traceability]]></category>
            <category><![CDATA[debugging]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:26:13 GMT</pubDate>
            <atom:updated>2022-01-05T06:26:13.486Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://github.com/google/gops">GoPS</a> is one alternative (also made by Google, other than <a href="https://golang.org/pkg/runtime/pprof/">pprof</a>) to measure, trace or diagnose the performance and memory usage your <a href="http://golang.org">Go</a>-powered service/long-lived program. The usage is very easy, you just need to import and add 3 lines in your main (so the gops command line can communicate with your program):</p><p><strong>import “github.com/google/gops/agent”</strong></p><p>If you don’t put those lines, you can still use gops limited to get list of programs running on your computer/server that made with Go with limited statistics information, using these commands:</p><p><strong>$ go get -u -v github.com/google/gops</strong></p><p><strong>$ gops # show the list of running golang program</strong></p><p><strong>$ gops tree # show running process in tree</strong></p><p><strong># PID can be replaced with GOPS host:port of that program</strong></p><p><strong>$ gops stack PID # get current stack trace of running PID</strong></p><p><strong>$ gops memstats PID # get memory statistics of running PID</strong></p><p><strong>$ gops gc PID # force garbage collection</strong></p><p><strong>$ gops pprof-cpu PID # get cpu profile graph</strong></p><p><strong>$ gops pprof-heap PID # get memory usage profile graph</strong></p><p><strong>profile saved at /tmp/heap_profile070676630</strong></p><p><strong>$ gops trace PID # get 5 sec execution trace</strong></p><p><strong># you can install graphviz to visualize the cpu/memory profile</strong></p><p><strong>$ sudo apt install graphviz</strong></p><p><strong># visualize the cpu/memory profile graph on the web browser</strong></p><p><strong>$ go tool pprof /tmp/heap_profile070676630</strong></p><p>Next step is analyze the call graph for the <a href="https://www.freecodecamp.org/news/how-i-investigated-memory-leaks-in-go-using-pprof-on-a-large-codebase-4bec4325e192/">memory leaks</a> (which mostly just wrongly/forgot to defer body/sql rows or holding slice reference of huge buffer or certain framework’s <a href="https://rover.rocks/golang-memory-leak/">cache trashing</a>) or <a href="https://blog.golang.org/pprof">slow functions</a>, whichever your mission are.</p><p>What if golang service you need to trace it inside <a href="https://kubernetes.io/">Kubernetes</a> pod that the GOPS address (host:port) not exposed to outside-world? Kubernetes is a popular solution for companies that manages bunch of servers/microservices or cloud like (GKE, AKS, Amazon EKS, ACK, DOKS, etc) but obviously overkill solution for small companies that doesn’t need to scale elastically (or the servers are less than 10 or not using microservice architecture).</p><p>First, you must compile gops statically so it can be run inside alpine container (which mostly what people use):</p><p><strong>$ cd $GOPATH/go/src/github.com/google/gops</strong></p><p><strong># copy gops to your kubernetes pod</strong></p><p><strong>$ kubectl cp ./gops $POD_NAME:/bin</strong></p><p><strong>$ kubectl exec -it $POD_NAME — sh</strong></p><p><strong># for example you want to check heap profile for PID=1</strong></p><p><strong># copy back trace file to local, then you can analyze the dump</strong></p><p><strong>kubectl cp $POD:/tmp/heap_profile070676630 out.dump</strong></p><p>But if your address and port are exposed you can directly use gops from your computer to the pod or create a tunnel inside the pod if it doesn’t have public IP, for example using <a href="https://ngrok.com/">ngrok</a>.</p><p>Btw if you know any companies migrating from/to certain language (especially Go), frameworks or database, you can contribute <a href="https://github.com/kokizzu/list-of-tech-migrations">here</a>.</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2021/01/gops-trace-your-golang-service-with-ease.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b7e2a62f24b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pyroscope: Continuous Tracing in Go, Python, or Ruby]]></title>
            <link>https://kokizzu.medium.com/pyroscope-continuous-tracing-in-go-python-or-ruby-6b7a87186a1d?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/6b7a87186a1d</guid>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[traceability]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[profiling]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:24:45 GMT</pubDate>
            <atom:updated>2022-01-05T06:24:45.550Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently I stumbled upon slow library/function problem and don’t know chich part that causes it, and found out that there’s a easy way to trace either Go, Ruby, or Python code using <a href="https://pyroscope.io/">Pyroscope</a>. The feature is a bit minimalist, there’s no memory usage tracing yet unlike in <a href="http://kokizzu.blogspot.com/2021/01/gops-trace-your-golang-service-with-ease.html">gops</a> or <a href="https://blog.golang.org/pprof">pprof</a>. Pyroscope consist 2 parts: the server and the agent/client library (if using Golang) or executor (if using Ruby or Python). Here’s the way how to run and start Pyroscope server:</p><p><strong># run server using docker</strong></p><p><strong>docker run -it -p 4040:4040 pyroscope/pyroscope:latest server</strong></p><p>And here’s the example on how to use the client library/agent (modifying Go’s source code, just like in <a href="https://docs.datadoghq.com/getting_started/">DataDog</a> or any other <a href="https://raygun.com/blog/apm-tools/">APM tools</a>) and install the Pyroscope CLI to run Ruby/Python scripts:</p><p><strong># golang, add agent inside the source code</strong></p><p>It would show something like this if you open the server URL (localhost:4040) in the browser, so you can check which part of the code that took most of the runtime.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*T6vz0V9WN28R8zrm.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*WcwMSckWRrHjdJHm.png" /></figure><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2021/03/pyroscope-continuous-tracing-in-go.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6b7a87186a1d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kubernetes IDE/GUI]]></title>
            <link>https://kokizzu.medium.com/kubernetes-ide-gui-171f9f2c0beb?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/171f9f2c0beb</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[gui]]></category>
            <category><![CDATA[ide]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:23:09 GMT</pubDate>
            <atom:updated>2022-01-05T06:23:09.132Z</atom:updated>
            <content:encoded><![CDATA[<p>There’s various GUI for Kubernetes that I’ve found:</p><ul><li><a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/">The default kubernetes dashboard</a></li><li><a href="https://github.com/vmware-tanzu/octant">Octant</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*0dzLOCan40xVaedC.png" /></figure><ul><li><a href="https://www.jetbrains.com/help/idea/services-tool-window.html">IntelliJ Service Tool Window</a> (View &gt; Tool Window &gt; Services, Alt+8)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*uxAtfeTkkHlaJIBY.png" /></figure><ul><li><a href="https://cloud.google.com/code/docs/intellij/using-the-kubernetes-explorer">IntelliJ Cloud Code</a> (most features focused on Google Cloud and Google Cloud projects)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/346/0*-ZvV5MVlnck77Da9.png" /></figure><ul><li><a href="https://docs.k8slens.dev/latest/getting-started/">Kontenta-Lens</a> (most beautiful one :3)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*06hdDZZxmdPYgP32.png" /></figure><ul><li><a href="https://github.com/kubernetes-sigs/kui">Kui</a> (like jupyter-notebook, pry/irb/julia, etc, have tutorial mode)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/298/0*fHqtv79-0Tkk-47o.png" /></figure><ul><li><a href="https://github.com/astefanutti/kubebox">KubeBox</a> (terminal based GUI)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*y5g30wRGZ-aOlAOt" /></figure><ul><li><a href="https://k8dash.io/">k8dash</a> (deployment only)</li><li><a href="https://github.com/kinvolk/headlamp/releases">HeadLamp</a> (didn’t work)</li><li><a href="https://k9scli.io/">K9s</a> (failed to go get, also require license?)</li></ul><p>For now my recommendation would be Kontenta-Lens, you’ll get a bird-view of your cluster.</p><p>For shell autocomplete, I recommend <a href="https://github.com/c-bata/kube-prompt">kube-prompt</a> (or other shell-specific autocomplete)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/830/0*BgvOGdy264SlY50q.gif" /></figure><p>If you only need docker GUI, you can try <a href="https://moncho.github.io/dry/">Dry</a></p><p>If you prefer web-based GUI, you can try <a href="http://portainer.io/">Portainer</a> (it could manage much more than just kubernetes, but also local docker and docker swarm), it’s quite better than <a href="http://rancher.com/">Rancher</a>.</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2021/03/kubernetes-gui.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=171f9f2c0beb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mock vs Fake and Classical Testing]]></title>
            <link>https://kokizzu.medium.com/mock-vs-fake-and-classical-testing-9386cede093f?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/9386cede093f</guid>
            <category><![CDATA[mocking]]></category>
            <category><![CDATA[unit-testing]]></category>
            <category><![CDATA[fake]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:19:23 GMT</pubDate>
            <atom:updated>2022-01-05T06:19:23.319Z</atom:updated>
            <content:encoded><![CDATA[<p>Motivation of this article is to promote less painful way of testing, structuring codes, and less broken test when changing logic/implementation details (only changing the logic not changing the input/output). This post recapping past ~4 years compilation of articles that conclude that Fake &gt; Mock, Classical Test &gt; Mock Test from other developers that realize the similar pain points of popular approach (mock).</p><h4>Mock Approach</h4><p>Given a code like this:</p><pre><strong>type Obj struct {<br> *sql.DB // or Provider<br>}<br>func (o *Obj) DoMultipleQuery(in InputStruct) (out OutputStruct, err error) {<br> ... = o.DoSomeQuery()<br> ... = o.DoOtherQuery()<br>}</strong></pre><p>I’ve seen code to test with mock technique like this:</p><pre><strong>func TestObjDoMultipleQuery(t *testing.T) {<br> o := Obj{mockProvider{}}<br> testCases := []struct {<br> name string<br> mockFunc func(sqlmock.Sqlmock, *gomock.Controller) in InputStruct out OutputStruct<br> shouldErr bool<br> } {<br> {<br> name: `bast case`,<br> mockFunc: func(db sqlmock.SqlMock, c *gomock.Controller) {<br> db.ExpectExec(`UPDATE t1 SET bla = \?, foo = \?, yay = \? WHERE bar = \? LIMIT 1`).<br> WillReturnResult(sqlmock.NewResult(1,1))<br> db.ExpectQuery(`SELECT a, b, c, d, bar, bla, yay FROM t1 WHERE bar = \? AND state IN \(1,2\)`).<br> WithArgs(3).<br> WillReturnRows(sqlmock.NewRows([]string{&quot;id&quot;, &quot;channel_name&quot;, &quot;display_name&quot;, &quot;color&quot;, &quot;description&quot;, &quot;active&quot;, &quot;updated_at&quot;}).<br> AddRow(&quot;2&quot;, &quot;bla2&quot;, &quot;Bla2&quot;, &quot;#0000&quot;, &quot;bla bla&quot;, &quot;1&quot;, &quot;2021-05-18T15:04:05Z&quot;).<br> AddRow(&quot;3&quot;, &quot;wkwk&quot;, &quot;WkWk&quot;, &quot;#0000&quot;, &quot;wkwk&quot;, &quot;1&quot;, &quot;2021-05-18T15:04:05Z&quot;))<br> ...<br> }, in: InputStruct{...}, out: OutputStruct{...},<br> wantErr: false,<br> },<br> {<br> ... other cases<br> },<br> } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T){ ... // prepare mock object o := Obj{mockProvider} out := o.DoMultipleQueryBusinessLogic(tc.in) assert.Equal(t, out, tc.out) }) }<br>}</strong></pre><p>This approach has pros and cons:</p><p>+ could check whether has typos (eg. add one character in the original query, this test would detect the error)</p><p>+ could check whether some queries are properly called, or not called but expected to be called</p><p>+ unit test would always faster than integration test</p><p>- testing implementation detail (easily break when the logic changed)</p><p>- cannot check whether the SQL statements are correct</p><p>- possible coupled implementation between data provider and business logic</p><p>- duplicating work between original query and the regex-version of query, which if add a column, we must change both implementation</p><p>For the last cons, we can change it to something like this:</p><p>This approach has pros and cons:</p><p>+ not deduplicating works (since it just a simplified regex of the full SQL statements</p><p>+ still can check whether queries properly called or not</p><p>+ unit test would always faster than integration test</p><p>- testing implementation detail (easily break when the logic changed)</p><p>- cannot detect typos/whether the query no longer match (eg. if we accidentally add one character on the original query that can cause sql error)</p><p>- cannot check correctness of the SQL statement</p><p>- possible coupled implementation between data provider and business logic</p><p>We could also create a helper function to replace the original query to regex version:</p><pre><strong>func SqlToRegexSql(sql string) string {<br> return // replace special characters in regex (, ), ?, . with escaped version<br>}<br>db.ExpectQuery(SqlToRegexSql(ORIGINAL_QUERY)) ...</strong></pre><p>This approach has same pros and cons as previous approach.</p><h4>Fake Approach</h4><p>Fake testing use classical approach, instead of checking implementation detail (expected calls to dependency), we use a compatible implementation as dependency (eg. a slice/map of struct for database table/DataProvider)</p><p>Given a code like this:</p><p>It’s better to make a fake data provider like this:</p><p>So in the test, we can do something like this:</p><p>This approach have pros and cons:</p><p>+ testing behavior (this input should give this output) instead of implementation detail (not easily break/no need to modify the test when algorithm/logic changed)</p><p>+ unit test would always faster than integration test</p><p>- cannot check whether the queries are called or not called but expected to be called</p><p>- double work in Golang (since there’s no generic/template yet, go 1.18 must wait Feb 2022), must create minimal fake implementation (map/slice) that simulate basic database table logic, or if data provider not separated between tables (repository/entity pattern) must create a join logic too — better approach in this case is to always create Insert, Update, Delete, GetOne, GetBatch instead of joining.</p><p>+ should be no coupling between queries and business logic</p><p>Cannot check whether queries in data provider are correct (which should not be the problem of this unit, it should be DataProvider integration/unit test’s problem, not this unit)</p><h4>Classical Approach for DataProvider</h4><p>It’s better to test the queries using classical (black box) approach integration test instead of mock (white box), since mock and fake testing can only test the correctness of business logic, not logic of the data provider that mostly depend to a 2nd party (database). Fake testing also considered a classical approach, since it test input/output not implementation detail.</p><p>Using <a href="https://github.com/ory/dockertest">dockertest </a>when test on local and gitlab-ci service when test on pipeline, can be something like this:</p><p>Where the prepareDb function can be something like this (taken from dockertest <a href="https://github.com/ory/dockertest">example</a>):</p><p>In the pipeline the file can be something like this for PostgreSQL (use tmpfs/inmem version for database data directory to make it faster):</p><p>The dockerfile for tmpfs database if using MySQL can be something like this:</p><p>Or for MongoDB:</p><p>The benefit of this classical integration test approach:</p><p>+ high confidence that your SQL statements are correct, can detect typos (wrong column, wrong table, etc)</p><p>+ isolated test, not testing business logic but only data provider layer, also can test for schema migrations</p><p>- not a good approach for database with eventual consistency (eg. Clickhouse)</p><p>- since this is an integration test, it would be slower than mock/fake unit test (1–3s+ total delay overhead when spawning docker)</p><h4>Conclusion</h4><ol><li>use mock for databases with eventual consistency</li><li>prefer fake over mock for business logic correctness because it’s better for maintainability to test behavior (this input should give this output), instead of implementation details</li><li>prefer classical testing over mock testing for checking data provider logic correctness</li></ol><h4>References</h4><p>(aka confirmation bias :3)</p><p><a href="https://martinfowler.com/articles/mocksArentStubs.html">https://martinfowler.com/articles/mocksArentStubs.html</a><br><a href="https://stackoverflow.com/questions/1595166/why-is-it-so-bad-to-mock-classes">https://stackoverflow.com/questions/1595166/why-is-it-so-bad-to-mock-classes</a><br><a href="https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a">https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a</a><br><a href="https://chemaclass.medium.com/to-mock-or-not-to-mock-af995072b22e">https://chemaclass.medium.com/to-mock-or-not-to-mock-af995072b22e</a> <br><a href="https://accu.org/journals/overload/23/127/balaam_2108/">https://accu.org/journals/overload/23/127/balaam_2108/</a> <br><a href="https://news.ycombinator.com/item?id=7809402">https://news.ycombinator.com/item?id=7809402</a> <br><a href="https://philippe.bourgau.net/careless-mocking-considered-harmful/">https://philippe.bourgau.net/careless-mocking-considered-harmful/</a> <br><a href="https://debugged.it/blog/mockito-is-bad-for-your-code/">https://debugged.it/blog/mockito-is-bad-for-your-code/</a> <br><a href="https://engineering.talkdesk.com/double-trouble-why-we-decided-against-mocking-498c915bbe1c">https://engineering.talkdesk.com/double-trouble-why-we-decided-against-mocking-498c915bbe1c</a> <br><a href="https://blog.thecodewhisperer.com/permalink/you-dont-hate-mocks-you-hate-side-effects">https://blog.thecodewhisperer.com/permalink/you-dont-hate-mocks-you-hate-side-effects</a><br><a href="https://agilewarrior.wordpress.com/2015/04/18/classical-vs-mockist-testing/">https://agilewarrior.wordpress.com/2015/04/18/classical-vs-mockist-testing/</a><br><a href="https://www.slideshare.net/davidvoelkel/mockist-vs-classicists-tdd-57218553">https://www.slideshare.net/davidvoelkel/mockist-vs-classicists-tdd-57218553</a><br><a href="https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists">https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists</a><br><a href="https://stackoverflow.com/questions/184666/should-i-practice-mockist-or-classical-tdd">https://stackoverflow.com/questions/184666/should-i-practice-mockist-or-classical-tdd</a> <br><a href="https://swizec.com/blog/what-i-learned-from-software-engineering-at-google/#stubs-and-mocks-make-bad-tests">https://bencane.com/2020/06/15/dont-mock-a-db-use-docker-compose/<br>https://swizec.com/blog/what-i-learned-from-software-engineering-at-google/#stubs-and-mocks-make-bad-tests</a> <br><a href="https://www.freecodecamp.org/news/end-to-end-api-testing-with-docker/">https://www.freecodecamp.org/news/end-to-end-api-testing-with-docker/</a><br><a href="https://medium.com/@june.pravin/mocking-is-not-practical-use-fakes-e30cc6eaaf4e">https://medium.com/@june.pravin/mocking-is-not-practical-use-fakes-e30cc6eaaf4e</a> <br><a href="https://www.c-sharpcorner.com/article/stub-vs-fake-vs-spy-vs-mock/">https://www.c-sharpcorner.com/article/stub-vs-fake-vs-spy-vs-mock/</a></p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2021/07/mock-vs-fake-and-classical-testing.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9386cede093f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Against Golang Interface{Method}-abuse/pollution]]></title>
            <link>https://kokizzu.medium.com/against-golang-interface-method-abuse-pollution-f48c802855a0?source=rss-727b036791cf------2</link>
            <guid isPermaLink="false">https://medium.com/p/f48c802855a0</guid>
            <category><![CDATA[go]]></category>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[interfaces]]></category>
            <dc:creator><![CDATA[K Prayogo]]></dc:creator>
            <pubDate>Wed, 05 Jan 2022 06:17:15 GMT</pubDate>
            <atom:updated>2022-01-05T06:17:15.351Z</atom:updated>
            <content:encoded><![CDATA[<p>As you already know, after doing a lot of maintenance work of other people’s code, I don’t like to follow blindly so called “best practice” or popular practice that are proven painful in the long run when it’s followed blindly/doesn’t fit project’s use case, eg.</p><ul><li>using dynamically-typed language (JS, Python, PHP, Ruby, etc) just because it’s the most popular language — only for short/discardable project</li><li><a href="http://kokizzu.blogspot.com/2021/07/mock-vs-fake-and-classical-testing.html">mocking</a> — there’s better way</li><li>microservice without properly splitting domain — modular monolith is better for small teams, introducing network layer just to split a problem without properly assessing surely will be a hassle in a short and long run</li><li>overengineering — eg. adding stack that you don’t need when current stack suffice, for example, dockerizing or <a href="https://kokizzu.blogspot.com/2021/08/you-dont-need-kubernetes.html">kubernetesizing</a> just because everyone using it, adding ElasticSearch just because it’s search use case, but the records needs to be searched are very little and rps are very low, a more lightweight aproach more make sense: eg. TypeSense or MeiliSearch or even database’s built-in FTS are more make sense for lower rps target/simpler search feature.</li><li>premature “clean architecture” — aka. <a href="https://www.youtube.com/watch?v=IRTfhkiAqPw">over-layering</a> everything that you’ll are almost never replace — dependency tracking is better</li><li>unevaluated standard — sticking with standard just because it’s a standard, just like being brainwashed/peer-pressured by dead people’s will (tradition) without rethinking is it still make sense to be followed for this use case?</li><li>not making SRS/Software Requirement Specification (roles/who can do what action/API) and SDS/Software Design Specification (this action/API will mutate/command or read/query which datastore or hit which 3rd party) — this helps new guy to be onboarded to the project really fast</li></ul><p>I have one more unpopular opinion, <strong>interface</strong> (-overuse) in Golang is almost always bad for jumping around (jump to declaration-implementation) inside source code which causes everyday overhead when reading code and debugging. For example when you want to create a fake/mock/stub of certain method:</p><p><strong>type Bla interface { <br> Get(string) string<br> Set(string)<br>}</strong></p><p><strong>struct RealBla struct {} // wraps a 3rd party/client library<br>func (*RealBla) Get(string) string { return `` }<br>func (*RealBla) Set(string) { }</strong></p><p><strong>struct FakeBla struct {} // our fake/stub/mock implementation<br>func (*FakeBla) Get(string) string { return `` }<br>func (*FakeBla<br> var b Bla = FakeBla{…} <br> // usually as data member of other method that depends on RealBla<br> b.Set(…)<br> x := b.Get(…)</strong></p><p>the problem with this approach is, it’s harder to jump around between declaration and implementation (usually <strong>RealBla</strong> that we want, not <strong>FakeBla</strong>), how often we switch implementation anyway? <a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">YAGNI</a> (vs <a href="https://en.wikipedia.org/wiki/Overengineerin">overengineering</a>). It’s better for our cognitive/understanding that we keep both coupled, this violates single responsibility principle from SOLID, but it’s easier to reason/understand, since the real and fake are in the same file and near each other, so we can catch bug easily without have to switch, something like this:</p><p><strong>// declare/use 3rd party client here</strong></p><p><strong>UseFake bool<br> // create fake/in-mem here</strong></p><p>by doing this, we could compare easily between our fake and real implementation (you could easily spot the bug, whether your fake implementation differ way too much from real implementation), and we can still jump around simply by ctrl+click the IDE on that function since there’s only 1 implementation. The only pros I could see from doing <strong>interface</strong>-based is when you are creating a 3rd party library (eg. io.Writer, io.Reader, etc) and you have more than 2 implementation ( <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> only good when its more than 2), but since you’re only making this for internal project that could be easily refactored within the project itself, it doesn’t make sense to abuse <strong>interface</strong>. See more tips from this video: <a href="https://www.youtube.com/watch?v=ix0i65AOOIA">Go Worst Practice</a>.</p><p>After all being said, I won’t use this kind of thing ( <strong>UseFake</strong> property) for testing databases (2nd party), because I prefer to do integration (contract-based) testing instead of unit testing, since i’m using a fast database anyway (not a slow but popular RDBMSes).</p><p><em>Originally published at </em><a href="http://kokizzu.blogspot.com/2021/09/fight-against-golang-interfacemethod.html"><em>http://kokizzu.blogspot.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f48c802855a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>