Benki → Lazy Chat

⇠ previous page next page ⇢
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 kubeadm.alpha.kubernetes.io/cri-socket 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 docker.io, 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:

  <dependencies>
    ...
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-compiler</artifactId>
      <version>${scala.version}</version>
    </dependency>
    ...
  </dependencies>

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.

Matthias #

Das neue Benki (née Book Marx & Lafargue) ist jetzt voll funktionsfähig. Eine Handvoll Bugs habe ich noch notiert, aber im großen und ganzen funktioniert es besser als je zuvor.

Die folgenden Techniken kamen beim Rewrite zum Einsatz:

  • Quarkus. Davon insbesondere: RESTEasy, Hibernate, Qute.
  • Web Components. Und zwar ohne Frameworks wie Angular oder React. Nur browsernatives JavaScript. Sowohl selbstgeschriebene (z.B. MlkBookmarkSubmissionForm) wie auch welche aus der Web-Component-Bibliothek Elix.
  • Flow für die statische Prüfung des selbstgeschriebenen JavaScript-Codes. Das Gute an Flow (im Gegensatz zu TypeScript oder gar so etwas wie Reason oder ClojureScript) ist, daß man es ganz ohne Kompilierschritt in browsernativen JavaScript-Modulen benutzen kann, indem man Comment Types einsetzt.
  • Native CSS-Rasterlayouts.

Also alles Standards und Spezifikationen. Sehr Enterprise.

Matthias #

Wenn man NFS für Persistenz in Kubernetespods verwenden will und SELinux aktiviert hat, dann muß man die SELinux-Flags virt_use_nfs und virt_sandbox_use_nfs setzen:

setsebool -P virt_use_nfs 1
setsebool -P virt_sandbox_use_nfs 1

Wenn man das vergißt, kann man die Volumes zwar mounten, kriegt aber bei jedem Schreibversuch einen Zugriffsfehler.

Matthias #

Noch ein Hinweis für Benki-Benutzer: Da Mozilla den Persona-Dienst inzwischen eingestellt hat und Google OpenID nicht mehr unterstützt, ist ein Login beim Benki nur noch möglich, wenn man sich woanders eine OpenID anlegt und sie mir mitteilt, so daß ich sie in die Datenbank schreiben kann. Auch OpenID-Provider scheint es ja nicht mehr so viele zu geben. xlogon.net ist noch benutzbar und wäre eine mögliche Wahl.

Matthias #

Ich habe das Benki auf einen neuen Server umgezogen. Das hat reibungslos geklappt, und es ist, wie es scheint, voll einsatzbereit.

Eines habe ich bei der Gelegenheit allerdings abgeschafft, und zwar den WebID-Login. Der hat schon länger nicht mehr funktioniert, weil der komische node.js-basierte Frontendserver irgendwann spontan kaputtgegangen war, den ich zu dem Zweck schnell-schnell hingehackt hatte. Die WebID-IdP-Funktion ist aber nach wie vor aktiv, und das bleibt auch so. Man kann sich also beim Benki nicht mehr mit WebID einloggen, aber man kann das Benki nach wie vor als WebID-Provider verwenden und sich auf anderen Websites mit der WebID vom Benki einloggen. Also, abgesehen davon, daß ich auf die Schnelle jetzt keinen funktionierenden WebID-Login im ganzen Web mehr finden konnte. Die vom WebID-Wiki gehen jedenfalls anscheinend alle nicht mehr.

Matthias #

Lovers, of course, are notoriously frantic epistemologists, second only to paranoiacs (and analysts) as readers of signs and wonders. —Adam Phillips, On Flirtation (via Wikipedia)

Matthias #
(defn fix [f]
  (let [p (promise)]
    (deliver p (f p))
    @p))
⇠ previous page next page ⇢