使用 heredoc 語法的 Dockerfile

Simon Willison 這邊看到的,在 Dockerfile 裡面使用 heredoc 語法編 Docker image:「Introduction to heredocs in Dockerfiles」,引用的文章是「Introduction to heredocs in Dockerfiles」與「Introduction to heredocs in Dockerfiles」,七月的事情了。

heredoc 指的是可以讓開發者很方便使用多行結構,在 Dockerfile 這邊常見到的 pattern:

RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y ...

但這樣會產生出很多層 image,所以先前的 best practice 是:

RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y ...

而 heredoc 的導入簡化了不少事情,這應該有機會成為新的 best practice:

RUN <<EOF
apt-get update
apt-get upgrade -y
apt-get install -y ...
EOF

要注意的是,開頭要記得加上 #syntax 的宣告,用到 docker/dockerfile:1.3-labs 這組才能使用 heredoc:

# syntax=docker/dockerfile:1.3-labs

然後用 buildkit 去編,用新版的 Docker 已經包 buildkit v0.9.0 進去了:

DOCKER_BUILDKIT=1 docker build .

Fork 自微軟的 Pyjion 專案的 Python 3.10 + JIT 方案

Hacker News 上看到「Pyjion – A Python JIT Compiler (trypyjion.com)」這個專案,也是一個想要透過 JIT 加速 Python 的專案:

Pyjion is a drop-in JIT Compiler for Python 3.10. It can be pip installed into a CPython 3.10 installation on Linux, Mac OS X, or Windows.

看了一下是從微軟的 Pyjion 專案 fork 出來的,原來的專案最後一次 commit 是一年前,而且專案也已經標示為 archived (read-only mode),但有留下轉移的說明,也就是上面提到的專案:

Development has moved to https://github.com/tonybaloney/Pyjion

可以看到大部分的效能都已經進入改善階段 (很多導入 JIT 的專案在初期時會先變慢):

跟其他的 JIT 方案相比,Pyjion 的目標是高度相容現有 Python 的程式,包括各種 extension,這點的確是在用 PyPy 這些軟體時的痛點沒錯...

看起來透過 pip 裝好後就可以直接 import 進來用,後續就會生效:

import pyjion; pyjion.enable()

另外提一下,翻 Hacker News 留言的時候翻到這個害我笑出來,有夠新 XD

zatarc 3 days ago | unvote | prev | next [–]

Pyjion requires: CPython 3.10 and .NET 6

.NET 6 Release: 19 hours ago (https://github.com/dotnet/core/blob/main/release-notes/6.0/6...)

... ok.

用 PostgreSQL 的 int4range 與 GiST

發現自己根本還不熟悉 PostgreSQL 的特性,寫一下記錄起來。

產品上常常會有 coupon 與 voucher 之類的設計,這時候通常都會設定 coupon 或 voucher 的有效期間,在 MySQL 的環境下可能會這樣設計:

CREATE TABLE coupon (
  id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  code VARCHAR(255) NOT NULL,
  started_at INT UNSIGNED NOT NULL,
  ended_at INT UNSIGNED NOT NULL
);

另外是設計 index 的部份,在產品推出夠久後,通常是過期的 coupon 或 voucher 會比目前還有效的多,而還沒生效的 coupon 與 voucher 通常都不多,所以會設計成對 ended_at 放一組 B-tree index:

CREATE INDEX ON t1 (ended_at);

這個設計不算差,不過用了一些假設。

如果不想要用這些假設,可以改用 Spatial 的資料型態去模擬並且加上 index (使用到 LineString Class),這樣就直接對 a < x < b 這類查詢更有效率,不過缺點就是可讀性會比較差。

在 PostgreSQL 這邊就有更清晰的資料結構來處理這些事情,主要是有一般性的 int4rangeint8range 以及時間類的 tsrangetstzrangedaterange (參考「Range Types」這邊有更多資料型態),所以會變成:

CREATE TABLE coupon (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  code VARCHAR NOT NULL,
  active_at INT4RANGE NOT NULL
);

然後用 GIST 建立 index:

CREATE INDEX ON t1 USING GIST(active_at);

後續的 query 語法就用 <@ 的語法:

SELECT COUNT(*) FROM coupon WHERE 10000 <@ active_at;

塞了 10M 筆資料後的 table 可以看到本來需要的時間是:

Time: 779.542 ms

變成:

Time: 5.510 ms

不過缺點就是 SQLite 沒支援這些資料型態,對於 test case 就一定得跑個 PostgreSQL 起來測...

限制流量的方式 (rate limit)

Lobsters Daily 上看到這篇 2017 年的文章,Figma 的工程師講怎麼做 rate limit:「An alternative approach to rate limiting」,只要大一點的站台就會遇到 spammer 之類的攻擊,就會希望實做自動化的機制擋住 spammer。

文章裡面提到了三種方式,第一種 (類) 提到了經典的 Token bucketLeaky bucket,這邊文章提供的演算法是讓每個使用者都會有一筆資料紀錄在 Redis 裡面 (這邊的用法可以抽換換成 Memcached),裡面記錄了最後一次的 access time 以及還剩下多少 token 可以用,接下來就可以照時間計算 token 的補充與消耗:

但這個演算法的缺點是 race condition,需要另外設計一些機制確保操作的 atomic:

不過大多數的實做就算不管 atomic 也還行 OK,只是會比較不精確一點。

第二個方法他叫做 Fixed window counters,這個方法把時間切齊為單位 (像是 60 秒為一個 window),所以可以把起點的時間也放到 key 裡面,然後 value 就是數量:

這個作法的好處就是簡單,而且 Redis 與 Memcached 都有提供 atomic 的 +1 操作。但缺點是可能會發生兩倍以上的 request,像是 5 reqs/min 的限制有可能會有連續的一分鐘內達到 10 reqs/min:

不過我覺得就 antispam 來說算是夠用了,當年 (大概是 2007 或是 2008 年?) 在 PIXNET 時用 C 寫 Apache module 就是把資料丟到 Memcached 裡面就是這樣實做的,然後每次學術網路的實驗室跑來掃站的就會自動被擋 XDDD

第三種方式他們稱作 Sliding window log,就是把每個 request 的 timestamp 都存起來,這個部份用 Redis 的資料結構會比用 Memcached 方便一些:

這個方式在控制上更精確,不過空間成本上就高很多... 這樣算是把常見的實做方式都提到了。

Git 的 Diff 演算法

Lobsters Daily 上看到「When to Use Each of the Git Diff Algorithms」這篇 2020 的文章 (Lobsters 這邊常冒出一些舊文章),講 Git 的 diff 演算法。

文章裡面題到了四個演算法,看了一下 git-diff 的 manpage 也還是這四個:

--diff-algorithm={patience|minimal|histogram|myers}

另外看 manpage 時還有看到 --anchored 的用法,看起來是用來提示 Git 產生比較好懂的 diff 結果:

--anchored=<text>

Generate a diff using the "anchored diff" algorithm.

This option may be specified more than once.

If a line exists in both the source and destination, exists only once, and starts with this text, this algorithm attempts to prevent it from appearing as a deletion or addition in the output. It uses the "patience diff" algorithm internally.

回頭翻了一下自己的 config repository,看起來 2017 年年底的時候我就加了「Use histogram diff algorithm.」,然後過幾天換成「Use patience algorithm.」,就一直用到現在...

這次改用 minimal 跑一陣子看看好了...

挑 Coding 字型的網站

Hacker News Daily 上看到「Coding Font by Typogram – Find Your True Love of Coding Fonts」這個,可以直接用二選一的方式挑出自己喜歡的 coding 的字型...

不過我的習慣是偶而就會換個字型,對我來說更有用的是字型列表,另外網站也提供了界面可以看:

  • Anonymous Pro
  • Azeret Mono
  • B612 Mono
  • Courier Prime
  • Cousine
  • Cutive Mono
  • DM Mono
  • Fira Code
  • Fira Mono
  • IBM Plex Mono
  • Inconsolata
  • JetBrains Mono
  • Major Mono Display
  • Nanum Gothic Coding
  • Noto Sans Mono
  • Nova Mono
  • Overpass Mono
  • Oxygen Mono
  • PT Mono
  • Roboto Mono
  • Share Tech Mono
  • Source Code Pro
  • Space Mono
  • Syne Mono
  • Ubuntu Mono
  • VT323
  • Xanh Mono

裡面有些字型以前連用都沒用過,看起來可以找來用看看... 不過看了一輪發現有些字型應該不是為了 coding 用的,像是 Space Monofixed 就會把 fi 連在一起而且只佔一格:

然後我自己用 15px 玩了一輪選到了 Azeret Mono,看起來可以裝起來用看看...

RFC 3339 與 ISO 8601 用範例的比較

Hacker News 首頁上看到「RFC 3339 vs. ISO 8601 (ijmacd.github.io)」這個連結,原始網站是「RFC 3339 vs ISO 8601」。

兩個都是試著定義日期與時間的表達方式,其中 ISO 8601 是在 1988 年定義出來的,被廣泛應用在很多 spec 裡,但最大的問題就是 ISO 8601 沒辦法公開免費取得,這點在前幾個月也在 Hacker News 上被討論過:「ISO-8601 date format reference not publicly available (twitter.com/isostandards)」。

RFC 3339 是在 2002 年訂出來,其中一個目標就是試著解決 ISO 8601 不公開的問題 (沒講明就是了):

This document includes an Internet profile of the ISO 8601 [ISO8601] standard for representation of dates and times using the Gregorian calendar.

這份 RFC 3339 與 ISO 8601 的比較可以讓設計 spec 的人可以看一下,在大多數的情況下,RFC 3339 應該是夠用的...

YJIT 帶給 Ruby 大量的效能提昇

Hacker News 首頁上看到的消息,由 Shopify 贊助的 YJIT 被 Ruby 官方接受了:「Merge YJIT: an in-process JIT compiler (github.com/ruby)」。

YJIT currently provides average speedups of 23% over the CRuby interpreter on realistic benchmarks, and near-instant warm-up time.

實做 YJIT 的 Maxime Chevalier-Boisvert 在他自己的 blog 上有提到這次的實做:「YJIT: Building a New JIT Compiler Inside CRuby」,裡面選擇的方法是他的 PhD 論文:「Simple and Effective Type Check Removal through Lazy Basic Block Versioning」。

可以看到在六月寫文章的時候,改善其實還沒這麼大,而且作者提到有不少可以再提昇的空間:

That being said, according to our benchmarks, we’ve been able to achieve speedups over the CRuby interpreter of 7% on railsbench, 19% on liquid template rendering, and 19% on activerecord.

Currently, only about 50% of instructions in railsbench are executed by YJIT, and the rest run in the interpreter, meaning that there is still a lot we can do to improve upon our current results.

本來的 MJIT 看起來會慢慢淡出...

用 AWK 寫的 Git

前幾天看到的東西,用 AWK 寫的 Git:「Aho: A Git implementation in AWK.」,Hacker News 上的討論「A Git Implementation in Awk (github.com/djanderson)」也可以翻一下。

Aho 這個名字取自 AWK 的作者之一 Alfred Aho (AWK 中的 A),然後查資料才發現他剛拿到去年 2020 的 Turing Award...

作者提到了為什麼會用 AWK 寫 Git,看起來就是個爽字 XDDD:

I've had the irrational desire to write something substantial in AWK for a while. Figured I might as well learn some Git internals while I scratch this itch.

然後他有提到他沒打算把網路相關的功能實做進去:

I don't plan to add network functionality to this (even though you totally can), so no clone or push.

是個有趣的專案,寫爽的 XD

jQuery UI 與 jQuery Mobile 進入維護模式

OpenJS Foundation (先前的 jQuery Foundation) 宣佈 jQuery UIjQuery Mobile 進入維護模式:「jQuery maintainers update and transition jQuery UI as part of overall modernization efforts」、「jQuery maintainers continue modernization initiative with deprecation of jQuery Mobile」。

jQuery UI is in maintenance-only mode. Users should not expect any new releases, though patches may be issued to resolve critical security, interoperability, or regression bugs.

The team announced that the cross-platform jQuery Mobile project under its umbrella is fully deprecated as of October 7, 2021.

應該就是沒有足夠的動能繼續開發了,維持個窗口在有 security issue 時處理...