Benki → Lazy Chat

next page ⇢
Matthias #

I tried out GNOME 40 today.

I had already read about it online, with quite a few articles suggesting that it was going into the wrong direction UI-wise. One common complaint was that it seemed more touch-oriented and less efficient to use with a mouse.

After trying it out, I am pleasantly surprised. I find I am quite happy with the gesture-based input paradigm that GNOME 40 is built around. In particular, navigating between the activity overview and the main desktop has become significantly quicker for me.

Perhaps my fondness of the MacBook’s way of treating its trackpad as a first-class input device has rubbed off on me and I am now attuned to it. What is surprising, however, is that notwithstanding the substantial amount of time I have spent in macOS, I seem to prefer GNOME’s implemention over the Mac’s. It feels, to me, even more efficient and to the point.

Matthias #

Even with popular support for the CSU dwindling, it still seems unlikely for the left to capture a significant number of direct mandates (first-vote seats assigned by first-past-the-post voting) in Bavaria due to vote splitting between the three left-leaning parties (the Left Party, the Social Democrats, and the Greens).

If I were responsible for any of the three parties’ Bavarian election strategies, I would propose to the other two to hold a joint primary election in each voting district. This would likely improve the odds for both the Social Democrats and the Greens while not really changing them for the Left Party (while still giving them the benefit of hurting the other end of the political spectrum plus the opportunity to build alliances within their own).

Matthias #

The big issue that I have with how the German government does COVID politics is that they can’t seem to handle the trolley problem.

The default choice appears to be inaction. A decision to act is made only when there is (seemingly) irrefutable scientific consensus that it causes no harm. That is arguably the correct approach for a doctor to take on a patient who cannot make an informed decision for themselves. But a pandemic is more akin to war than to a doctor’s visit. There will be casualties, some of which innocent bystanders—the only question is how many. In that kind of situation, inaction is not the safe choice. There is no safe choice.

Matthias #

I just finished migrating my Mailcow installation from a native Kubernetes port that I had made by hand and that was becoming impossible to update to a more streamlined (albeit wasteful with resources) deployment where I wrap docker-compose and a dedicated Docker instance in a Kata container that I run as a Kubernetes pod.

The container is built as a Nix expression and is available in my public Kubeia repository, which contains part of my Kubernetes deployment configuration and image build scripts. A README is available too, in case you would like to try running it yourself.

Do note that the Kubernetes deployment file is just provided as an example. It contains some pretty specific references to my particular deployment – view it as something like a template that you will have to copy and fill with your own data. Another idiosyncracy is that I really really dislike running multiple database servers on a single piece of hardware and so I kluged something in that makes Mailcow use my already provisioned MariaDB instance rather than its own. In other words, your mileage may very much vary.

Matthias #

On unsafe Rust. A common misunderstanding is that unsafe Rust is more liberal than safe Rust. It is not. The same invariants apply, but instead of the compiler, it’s your job to uphold them.

Unsafe Rust is what you write when in any other language you would have written a C extension. It is a very rare thing to do.

Matthias #

Ich wurde kürzlich darauf hingewiesen, daß Doppelnennungen wie in „Studentinnen und Studenten“ nicht als inklusiv gelten, da sie nur Frauen und Männer einschließen, nicht aber Menschen, die sich als weder noch begreifen. Nur mit Gendersternchen sei es inklusiv: „Student*innen“.

Ich schlage eine Alternative vor. Wie wäre es, wenn wir wieder dazu übergingen, mehr angelsächsische Kultur zu übernehmen, und einfach „Studenten“ sagten? Revolutionär, ich weiß.

Matthias #

Remember: The closer we get to a vaccine, the easier it is to justify a stricter lockdown.

If the logic behind that statement doesn’t seem obvious to you, think about the extreme cases: If we were in a pandemic with no chance of ever getting rid of it or finding any sort of treatment, a lockdown would make little sense. Since everyone would contract the virus eventually, very few people would be saved by the measures, but more people would suffer or even die due to the economic consequences of a lockdown whose duration would have to be indefinite. If, on the other hand, we were just two weeks away from eradicating the pandemic at the snip of a finger, then it would clearly be the correct thing to do to impose a strict lockdown for those two weeks in order to maximize the number of lives saved, since each person who manages to avoid the virus for just another two weeks would be saved from it for good.

Matthias #

Guess what one of the top disk latency inducers is on my (functionally mostly idle) server.

# zfsslower
Tracing ZFS operations slower than 10 ms
TIME     COMM           PID     T BYTES   OFF_KB   LAT(ms) FILENAME
09:14:10 async_49       2675004 S 0       0          18.29 journal.jif

The mysteriously named async_49 represents Mnesia as used by RabbitMQ as part of… Zulip.

Have I mentioned that a Zulip instance hosting 3 users is a waste of resources? Oh, I have, haven’t I?

Matthias #

How do I fix CGit’s display of a repository’s time of last update?

If you copied Git repositories into CGit at one point, you may have done so without keeping their mtimes intact. In this case, CGit will display an incorrect time of last update for the affected repositories, as it does not determine it based on the most recent Git commit but rather the time the default branch was last touched on the local file system.

By default, CGit uses the mtime of refs/heads/master (assuming that master is your default branch) to determine the time of last update, so this is how you can fix the time to be the same as the commit date of the last commit:

touch -c refs/heads/master -t $(date +"%Y%m%d%H%M.%S" --date=@$(git show -s --format=%ct HEAD))
Matthias #

You can now subscribe to this web site via a weekly email newsletter. The content is the same as in the public news feed. Be warned: The code is beta quality. The very first issue of the newsletter is also going to contain all posts ever made up to this point.

Matthias #

On Reddit there is currently a discussion going on about the newly written Haskell committee guidelines for respectful communication. There’s nothing new about it—one side saying it’s long overdue, another saying it’s the usual overly draconian, one-sided snowflake nonsense, with little more nuanced commentary in between.

Now, I haven’t read the document being discussed nearly carefully enough and from the cursory look I’ve given it, it actually looks like one of the better ones that I can find little to disagree with in, so I’ll not be commenting on it specifically. But I do have an opinion on codes of conduct like it in general and I’ll make use of its wording to illustrate it (in part because it feels like such an okay code of conduct to me, all things considered).

So let’s look at codes of conduct in more generality.

In my opinion, while their goals are clearly noble and worthy of support, the concrete regulations stipulated in such documents often go way overboard and, if implemented, can be harmful and unfair to the parties involved.

To illustrate why, take the following statement the document linked above makes:

In our communication, we consistently honour and affirm the … good intentions of others.

Which sounds very reasonable. But then contrast it with this part:

Our response should usually be to apologise … Even if we feel we have been misinterpreted or unfairly accused, the chances are good there was something we could have communicated better …

There is an apparent contradiction here depending on how you read the word “should.” Under the assumption that person B accuses person A of insulting them, should we:

  1. assume that person A had only good intentions and therefore there is no need to apologize, or
  2. assume that person A is obliged to apologize because if in doubt, person B was probably right that person A said something wrong?

I think the fundamental problem lies in the three different forms that the communication in question takes and the nontrivial mappings between them:

                               ┌────┐  ┏━━━━━━━━━━━━━━┓   ┌────┐                   
╔═══════════════════╗          │g[B]│  ┃              ┃   │f[A]│                   
║realm of expression║         ┌┴────┴──┃ what is said ┃◀─┴────┴┐                  
╚═══════════════════╝         │        ┃              ┃         │                  
                              │        ┗━━━━━━━━━━━━━━┛         │                  
                              ▼                                │                  
                       ┏━━━━━━━━━━━━┳───────────┐       ┏━━━━━━━━━━━━┳───────────┐
╔═══════════════════╗  ┃  what is   │ Person B  │       ┃  what is   │ Person A  │
║ realm of meaning  ║  ┃ understood ┣───────────┘       ┃  intended  ┣───────────┘
╚═══════════════════╝  ┗━━━━━━━━━━━━┛                   ┗━━━━━━━━━━━━┛            

If what was said causes person B to be offended, do you automatically assume that what was intended was an offense? Or do you assume that if person A maintains that they did not mean to offend, do you completely discount what was understood? Is either extreme reasonable? Clearly not. Hence the contradictory phrasing in the document.

What you need to realize to untangle this mess is that both f and g are dependent on both the person executing them and the situation that they are in at the moment they do so (which I’m going to simplify notationally by assuming that the situation is a part of the person). With this realization we can now rephrase the problem:

How much responsibility do we put on person A to anticipate g[B] for any given person B?

Clearly we cannot expect person A to anticipate g[B] for all possible B in a discussion on a public mailing list, since the space {g[B] | B ∈ Audience} is, for all intents and purposes, infeasible to compute when Audience is sufficiently large. On the other hand it is also obvious that if what is said is in the context of a face-to-face meeting between two individuals who know each other well, the challenge is much simpler and we can expect person A to be more considerate of what they can reasonably expect person B to understand based on what they express.

(What people will actually do in practice when they do not know the audience well is to substitute B := A, which may seem overly simplistic, but is really as good an approximation as any when Audience = World.)

In practice, what this means is that as an outside observer C, the wise thing to do is probably to assume both that:

  1. when evaluation person B’s conduct, the only correct way to interpret what was said is in the most malevolent way reasonably interpretable, and
  2. when evaluating person A’s conduct, the only correct way to interpret what was said is in the most benevolent way reasonably interpretable.

Which results in the contradiction mentioned previously. As far as I know, there is no way to resolve it, so all we can do is accept it.

Matthias #

Zulip is ridiculous.

Here are the biggest RAM eaters in my Kubernetes cluster, all of which are pretty much completely idle right now:

NAME            CPU(cores)   MEMORY(bytes)
gerrit          5m           315Mi
keycloak        3m           440Mi
mulkcms2        2m           325Mi
zulip           31m          2698Mi

Remember, Keycloak is built on top of JBoss.

Let that sink in for a bit.

What’s going on there? Well, Zulip consists of 20 microservices. It’s the modern way.

Matthias #

How to switch from Docker to Containerd on a kubeadm-managed Kubernetes node:

  1. Install Containerd.

  2. Edit /etc/default/kubelet and add the following line:

    KUBELET_EXTRA_ARGS=--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock

    If KUBELET_EXTRA_ARGS exists already, add the additional parameters to it instead.

  3. Restart kubelet.

  4. Uninstall Docker.

  5. To make kubeadm aware of the change (so that kubeadm upgrade apply works): Let ${NODE_NAME} be the name of your node. Run:

    kubeadm edit nodes/${NODE_NAME}

    Look for the annotation. Change its value to /run/containerd/containerd.sock.

Now everything should be set up for Containerd and you can do such fun things as running Kubernetes pods in Kata containers.

Presumably, these instructions will work for a migration to CRI-O and other container runtimes as well, but I have not tried.

Matthias #

This website now has a search bar.

Since I am using PostgreSQL, which has a basic form of full text indexing built in, it was pretty easy to implement. You can find the implementation on Gerrit.

One interesting question was how to integrate a clause using the full-text search operator @@ into a Criteria query. I experimented a bit and found that if you define an IMMUTABLE function and use it in a query, PostgreSQL has no trouble inlining and optimizing it, so it’s a great way to call into PostgreSQL-specific functionality from within a Criteria query. For instance, it will make perfectly fine use of indices where possible:

mulkcms=# EXPLAIN
            SELECT cached_description_html 
              FROM benki.bookmark_texts
             WHERE post_matches_websearch(search_terms, 'en', 'test');

                        QUERY PLAN
 Bitmap Heap Scan on bookmark_texts
   Recheck Cond: (search_terms @@ '''test'''::tsquery)
   ->  Bitmap Index Scan on bookmark_texts_search_terms_idx
         Index Cond: (search_terms @@ '''test'''::tsquery)

Just as you expect from a high-quality database system.

Matthias #

How to run Docker in a Kubernetes pod powered by a Kata container:

  1. Make sure that you are running containerd >= 1.3.
  2. Configure containerd as described at Kata Containers as a Runtime Class in the Kata Containers documentation.
  3. Add privileged_without_host_devices = true to the [plugins.cri.containerd.runtimes.kata] section of containerd’s config.toml file. This ensures that privileged Kata containers can only access the guest VM managed by the Kata containers runtime and not also the host system.
  4. Create a Kubernetes pod running an ubuntu:20.04 container with securityContext: {privileged: true} set and runtimeClassName: kata. You may wish to double-check that host devices are really inaccessible (for example by checking whether the host’s root disk is visible in /dev) before you proceed.
  5. Enter the Kubernetes pod, install Docker by running apt update; apt install -y --no-install-recommends, and type dockerd --storage-driver=vfs. Docker should now be running.

If you are migrating an existing kubeadm-managed, Docker-based cluster to Containerd, see my post on how to migrate kubeadm to Containerd.

Matthias #

I have reconfigured the server running this web site. The Kubernetes cluster can now run pods in Kata containers.

My hope is that I can use the isolation that it provides to run docker-compose inside a pod, as I use certain software that ships with a docker-compose file, which up to now I have always had to laboriously translate into a set of native Kubernetes deployments each time a new version would come out.

We’ll see if it’s as easy as I am hoping.

Matthias #

If you use Quarkus with Scala and you get a java.lang.NoClassDefFoundError or java.lang.ClassNotFoundException when recompiling in development mode, try adding an explicit scala-compiler dependency to your POM:


This may help because Quarkus has an implicit dependency on a specific version of the compiler, which may not match the one that you are using in your project.

It worked for me, at least.

(I have since removed Scala from the MulkCMS 2 code base again as it only made the build time much longer without bringing in enough to make it worth it. Scala 3 might change that — we shall see.)

Matthias #

The German Federal Ministry of Health is considering a bill giving people having tested positive for COVID-19 antibodies a free pass on Corona-related social restrictions.

I’m not going to criticize that. It’s probably inevitable given that the restrictions are quite severe in their impact on fundamental rights and therefore hard to justify keeping up where they are unnecessary.

It does, however, raise the question of how to deal with the potential problem that this might incentivize people to get themselves infected in order to earn their freedom back. In my opinion, it would make a lot of sense to couple the introduction of a law that gives privileges to COVID-positive people with the establishment of Hero Hotels where people can choose to undergo controlled variolation.

Since people in Hero Hotels would then be under quarantine, the dangers for society would be limited. It would also greatly decrease their own risk. And it would help society reach herd immunity in lieu of a vaccine.

Matthias #

Since there has clearly been some confusion about this:

  • There is no such thing as an LTS version of OpenJDK. OpenJDK versions 8 and 11 aren’t special in any way.
  • Oracle declare certain versions of their commercial Oracle JDK distribution (which is an OpenJDK build) as LTS versions, which they support for an extended period of time for their paying customers. This has no bearing on OpenJDK except that Oracle may or may not continue to upstream bug fixes from Oracle JDK to OpenJDK.
  • Other vendors may or may not provide their own sort of long-term support for certain versions of their own OpenJDK distributions. For instance, the AdoptOpenJDK group have been following Oracle in what they declare as LTS versions so far. Azul have been doing the same but also declared certain intermediate versions (13 and 15 so far) as “medium-term support” releases whose end of support is at least 1.5 years after the next LTS is released (which greatly decreases the incentive to use an LTS version that is more than a year old).

Again, there is no such thing as an LTS version of OpenJDK. Likewise, there is no such thing as an LTS version of the Java programming language and standard library. There is also no guarantee that an upgrade from 11 to 17 will be any easier than from, say, 14 to 17 (in fact, the opposite is more likely to be true). Don’t just stick with 8 or 11 because somebody else does so. Choose your vendor wisely and then balance what level of support the vendor provides with your own needs and make an informed decision.

Matthias #

In the olden days, we wrote Java code, and then we wired it together with a super-nested web of XML.

Then someone invented XDoclet, which begot annotations, and we moved it all right into the code, and everything started making more sense.

In the hip new world, we write Java code, and then we wire it together with a super-nested web of YAML.

I hope someone invents YDoclet and we can move it all back into the code once again. That would make me happy.

Matthias #

Neulich habe ich ja die Decline of Usability erwähnt, nach der heutzutage auf Hübschheit in UIs mehr Wert gelegt wird als auf Benutzbarkeit. Ich habe ein bißchen darüber nachgedacht und mir fiel dann auf, daß ich mit dem Design des neuen Benkis in dieselbe Falle getappt bin. Daraufhin habe ich das Design jetzt etwas funktionaler gemacht. Relativ wenig nützliche Details wie die Uhrzeit eines Postings habe ich entfernt und das ganze Design kompakter gemacht, so daß sehr viel mehr auf einmal auf den Bildschirm paßt, ohne die Lesbarkeit deutlich zu beeinträchtigen. Uneingeschränkt glücklich bin ich mit dem Redesign noch nicht, aber es wird. Ist auf jeden Fall schon besser als vorher.

Matthias #

Zu QALYs vs. Behandlungsaussicht vs. Intuition. Angenommen, wir haben drei Patienten, die die Behandlung mit einem Beatmungsgerät brauchen und unterschiedliche Aussichten auf den Erfolg der Behandlung haben:

  • Ein 10-jähriges Kind, Erfolgsaussicht: 80%
  • Die 30-jährige Mutter des Kindes, Erfolgsaussicht: 85%
  • Einen 90-Jährigen, Erfolgsaussicht: 90%

Nehmen wir weiters an, alle drei Patienten hätten einen guten Gesundheitszustand und würden nach einer erfolgreichen Behandlung noch mindestens 1 Jahr leben, während sie ohne Behandlung definitiv sterben würden.

Wir haben 2 Beatmungsgeräte. Wer kriegt sie und warum?

Matthias #

Ich habe Gradle aus dem neuen Benki rausgeworfen und die custom Tasks, die es funktional vom Maven-POM unterschieden, in Ant-Targets überführt. Das war leichter, als ich erwartet hatte.

Bei der Gelegenheit habe ich auch gleich das Kubernetes-Deployment automatisiert. Irgendwie fühlt sich das in einem Ant-Skript besser aufgehoben an als in einer Buildbeschreibungsdatei.

Matthias #

Ich habe den Rubikon aus meinem Feedreader gestrichen. Ich brauche wirklich nicht täglich mehrere Coronavirus-Verschwörungstheorien in meinem Newsfeed.

Matthias #

Ich frage mich ja, ob die Coronakrise eine Auswirkung auf die Wohnungspreise in Deutschlands Ballungszentren haben wird.

next page ⇢