SQLAlchemyでAurora MySQLのfailover追従を行う

Aurora MySQLでfailoverが発生した時、primaryから脱落したインスタンスは、通常のMySQLであれば起動後に変更されない innodb_read_only というシステム変数が1になり、更新クエリが 1290 "The MySQL server is running with the --read-only option so it c…

OTLPのspan nameが情報量少なすぎる件

OpenTelemetry 公式の Instrumentation ライブラリを使っていると、Requestsのspanの名前が "GET" だけだったり、 mysqlclient のspanが "SELECT" だけだったりします。 たとえばGrafanaでトレースを見ていると "SELECT" ばかりが何個も並んでいて、一つ一つ…

NiquestsとRequestsの `pool_maxsize` の違い

前回の記事 ではNiquestsに最近修正された深刻なバグがあることを紹介しましたが、それで HTTP/2 や HTTP/3 の利用を不安に思い HTTP/1.1 を使うことにしていました。そして HTTP/1.1 の場合に Requests と pool_maxsize の振る舞いが大きく異なることに気が…

Niquestsの深刻なバグについての注意 (urllib3-future 2.15.902 で修正済み)

最近何度か紹介していた Niquests に致命的なバグがあり、問題の大きさの割にアナウンスが小さいので解説しておきます。 なお、このバグは urllib3-future 2.14.906 (2025-11-06) から発生し、2.15.902 (2026-02-03) で修正されました。 Niquests を使ってい…

`functools.cache` や `functools.lru_cache` をメソッドに使うメモリリークはruffで検出できる

functools.cacheをメソッドに使う - methaneのブログ で紹介した、普通に functools.cache をメソッドに使うとメモリリークになってしまう問題ですが、半年ぶり2回目遭遇したので再発防止しないとなと思ったらすでに静的チェックがありました。 docs.astral.…

メソッドのキャッシュには cached_method が便利

以前の記事で、functools.cache をそのままメソッドに使うとメモリリークになることと、その回避方法をいくつか紹介しました。 最後に紹介していた方法がこれです。 from functools import cache class A: def __init__(self, x): self._x = x self.f = cach…

uWSGIでマルチスレッド利用時のプロセス間ロードバランス

概要 私は、uWSGIを使う時は基本的にマルチプロセス、シングルスレッドの設定を推奨します。しかし、レスポンスタイムがときどき遅くなる外部API呼び出しを含む場合など、メモリ使用量やthundering herd問題を考慮しつつ多くの並列数が必要な場合にマルチス…

Pythonのもう一つのHTTPクライアント: Niquests

過去数回の記事で Requests や httpx の問題点や細かい挙動について触れてきました。これらのライブラリの代わりになるもう一つの有力なHTTPクライアントとして Niquests を紹介します。 NiquestsはRequestsのforkで、高い互換性を保ちながら、非同期処理やH…

httpxのパフォーマンス問題について

前回の記事でhttpxの検討を進めた後にこんな気になる記事を見かけたので現状を調査しました。 gfx.hatenablog.com 結論から言うと、これは httpx を async で利用する時の問題で、現状ではまだ解決されていません。同期APIを使っていれば問題ありません。 原…

requestsで長時間Sessionを使う場合はidle_timeoutに注意

Pythonで一番人気のあるHTTPクライアントライブラリはrequestsですが、requestsやその低レイヤーであるurllib3はidle_timeoutの設定を持っていないので、長時間アイドルが続いた接続を再利用した時に Connection Reset by Peer エラーが発生することがありま…

functools.cacheをメソッドに使う

functools.cache は便利ですが、メソッドに対して使う時には注意が必要です。 from functools import cache class A: @cache def f(self, x): return x * 2 for i in range(1000): a = A() a.f(42) print(A.f.cache_info()) # CacheInfo(hits=0, misses=1000…

コネクションプールなしでhttpxを使う場合の高速化

httpx をコネクションプールありで使う場合は client = httpx.Client() して client.get() などを使いますが、コネクションプールなしで使う場合は httpx.get() などを使います。 この httpx.get() のような単発でHTTPリクエストを実行するAPIは実際には内部…

ThreadPoolExecutorの終了処理

しかし、たとえば標準ライブラリの concurrent.futures.ThreadPoolExecutor はdamonスレッドを使っていません。 そのため、 executor.shutdown(wait=True) を atexit から呼び出すことができません。atexitで終了させるスレッドはdaemonにしよう - methaneの…

タイプヒントには「実装の最小要件」ではなく「想定範囲」を表す型を書く

リストを受け取ってループで処理する関数を実装するとき、引数のタイプヒントに list ではなく最小の要求として Iterable を書くことを好む人がいる。コードの実装が引数に対して必要としている最小要件(必要十分条件)を表すためだ。 def func(arg: Iterable…

atexitで終了させるスレッドはdaemonにしよう

なにかの処理をバックグラウンドスレッドで実行して、アプリケーション終了時にその処理を止めたいことがあります。 たとえばOpenTelemetryのトレースやログを送信するためにスレッドが使われていますが、それらは終了時にバッファリングしているデータを送…

コンテナのPythonからMySQLにzstd圧縮を有効にして接続する

mysql-connector-python を使えば簡単なのですが、あえて mysqlclient を使う場合の話です。 まず、Pythonの公式DockerイメージはDebianベースになっていますが、Debianではデフォルトではapt-getでMySQLをインストールできません。 (Debian sid では MySQL …

Go 1.24 の testing.B.Loop() が便利

Benchmark関数を書くとき、重い初期化処理が必要になるケースがある。 例えばデータベースから大量のデータを読み込むベンチマークを書きたい場合、まずはその大きいデータを持ったテーブルを作る必要がある。 Goのベンチマーク関数は、普通は実行時間がター…

Rows.Scan() に渡す変数はループ外で宣言した方が速い

go-mysql-driver のアロケーションを調査していて気づいた小ネタ。 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -423,9 +423,9 @@ func BenchmarkReceiveMassiveRows(b *testing.B) { b.Errorf("failed to select: %v", err) return } + var i int …

MySQLとMariaDBで接続collationが決定される方法の違い

MySQL 8.0がutf8mb4のデフォルトcollationを utf8mb4_0900_ai_ci に変更したことは有名です。 MariaDBも11.5からデフォルトcollationを変更したのですが、 utf8mb4_0900_ai_ci ではなく utf8mb4_uca1400_ai_ci になりました。 この2つのcollationについてはM…

PEP 781: Adding __type_checking__ constant を書いた

まだPR段階なので peps.python.org では表示されていません。Discussionはこちらです。 PEP 781: Adding __type_checking__ constant - PEPs - Discussions on Python.org 今までもtypingのインポートを避けるために from typing import TYPE_CHECKING の代…

ssh で Host / hostname をエイリアスに使いつつ Match host で設定する

兄弟のように同じような設定でログインするサーバーが複数台あるとする。 Host *.myproj.example.com でまとめて設定できるものの、 ssh コマンドでホスト名を指定するのが面倒だ。 その場合、Host と hostname で短いエイリアスを作ることができるが、そう…

Python 3.13 + PyMySQL で MySQL にSSL接続できない

Python 3.13 を PyMySQL のCIに追加したら、次のようなエラーが発生した。 "Can't connect to MySQL server on '127.0.0.1' ([SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Missing Authority Key Identifier (_ssl.c:1018))" 調べてみると…

setuptoolsで作ったwheelをPyPIにアップロードできない

mysqlclientの新バージョンをリリースするために uv publish *.whl したら、次のようなエラーが発生した。 Uploading mysqlclient-2.2.7-cp310-cp310-win_amd64.whl (202.9KiB) error: Failed to publish `mysqlclient-2.2.7-cp310-cp310-win_amd64.whl` to …

OpenTelemetryの計装ができているか確認する

OTLPを使うときによく opentelemetry.sdk.trace.export.BatchSpanProcessor を使うが、このモジュールには計装ができているかを簡単に確認できる SimpleSpanProcessor と ConsoleSpanExporter がある。 # ... from opentelemetry.sdk.trace.export import Ba…

太陽光発電の統合コストを読み解く

第7次エネルギー基本計画に向けて、2040年の発電コストの試算が発表された。 第5回 発電コスト検証ワーキンググループ|資源エネルギー庁 エネルギー基本計画は3年ごとに直されるが、今回の発電コスト試算で大きなトピックは、二酸化炭素排出コストと統合コ…

MariDB Connector/C がデフォルトでSSLを強制するようになった

MariaDB 11.4 がGAになり、SSLが自動で有効になるようになりました。合わせてMariaDB Connector/Cも3.4からデフォルトでSSLを利用し、かつサーバーの証明書を検証するようになりました。 これにより、MariaDB Connector/C 3.4以降のライブラリを利用してMySQ…

小さい文字列からASCII外のバイトを見つけたい

Pythonの内部処理で、与えられたUTF-8の文字列がASCII文字のみを含むかをテストするコードを書いています。 size_t型が8バイトの時、ポインタを char* から size_t* にキャストして、0x8080808080808080ullと論理積を取れば8バイトを一度に処理できます。 単…

"Missing Signed-By in the sources.list(5) entry for" を解消する

開発機をUbuntu 24.04 にアップデートしてしばらく経って、 apt update したときに表示される次のwarningが今更気になった。 N: Missing Signed-By in the sources.list(5) entry for 'http://ftp.riken.go.jp/Linux/ubuntu' 試しに /etc/apt/sources.list …

ワイヤレスイヤホンの高音質通話をやっと体験できた

SOUNDPEATS Air4 Pro というイヤホンを使っていて、結構通話品質が良い。去年発売のモデルだと結構有名なメーカーの製品でも金属管の中を通したかのようなエコーがかかる製品があったりするけれども、このイヤホンはかなり自然な音で通話できる。今はセール…

Python 3.12.7 で msgpack におけるメモリリークが修正された

github.com Python 3.12から参照カウントを固定化するImmortal Objectが導入されたのですが、それがinterned string全てを強制的に固定参照カウントにしてしまっていました。 Pythonの文字列はimmutableなので、短くて大量に同じ値が出てくる可能性がある文…

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。