フレッツの IPv6 を Ubuntu + Open vSwitch でパススルーさせて使う

自宅では長らくフレッツ + YBB を用い、 YBB 支給の光BBユニットによる v4 over v6 でインターネットに接続してきたが、色々面倒なので長らく IPv6 はルーターで遮断し、 IPv4 のみで使っていた。今回、サーバがいるネットワークは一旦除くとして、 PC や携帯電話といったクライアントがいるネットワークについては IPv6 も使えるようにすることにした。

ひかり電話は契約していないので、 IPv6 は /64 のネットワークが一つだけ利用できる。そのため、これ以上ネットワークを分割することはできないので、インターネット接続が可能な IPv6 ネットワークは、全てブリッジ(嘘)で接続する必要がある。しかし、現在 IPv6 でサーバーは立てていないはずではあるものの、一応 LAN の中にあるようなサーバーには、明示的に開けたもの以外は NGN から直接接続できないようにしておきたい。

現在ルーターとしては、YANLING IBOX-501 N13 の Celeron 3855U モデル に Ubuntu 20.04 を入れて利用している。なお、購入履歴を見ると2020年7月に $151.38 で買っているが、今見るとこれ自体はもう購入できないようだ。既にここに繋がっているネットワークに対し、 ONU から IPv6 ネットワークを引くことにする。

なお、光BBユニットではなく ONU 側から Ubuntu ルーターにケーブルを引いたので、光BBユニット特有の事情はここでは登場しない。ただし、 YBB 特有の事情が完全に絡んでいないかどうかはわからない。

Open vSwitch でブリッジ(嘘)を構成する

L2 ブリッジ(嘘)を Open vSwitch で構成する。

まずは

apt install openvswitch-switch

しておく。

(書き忘れていたので追記)今回の構成で、 openvswitch を restart したときに、 bridge と port が残って flow だけ消えて L2 ブリッジ(本当)として動作する状態に戻ってしまい、そのままではセキュリティ上問題がありそうだった。とりあえず、 restart 時に bridge を削除する設定はあったので、これを入れた。

$ grep OVS_CTL_OPTS /etc/default/openvswitch-switch
# OVS_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
OVS_CTL_OPTS=--delete_bridges=yes

netplan の yaml でブリッジを追加する。br_flets には ONU にブリッジ(本当)で繋がるように設定し、 br_homelan はもとから自宅のネットワークとして使っていたものを想定している。(br_homelan はもとからあったもので、 IPv4 とデュアルスタックのため、 IPv4 の設定が書かれている。)

network:
  bridge:
    ...
    br_homelan:
      interfaces:
      - enpXXXXX
      addresses:
      - 192.168.XXX.XXX/24
      dhcp6: false
      accept-ra: false
      link-local: []
      nameservers: {}
    br_flets:
      interfaces:
      - enpXXX
      accept-ra: false

Open vSwitch で br_flets から br_homelan に IPv6 を、基本的には中から外への通信を許可して最低限の ICMPv6 通信を許可する想定で書いてみる。ただし、これは最終的なスクリプトからこの時点で必要なものを抜粋したもの。なお、このスクリプトを書くには ovs-actions(7) – Linux manual page を参照したが、 table は 0 のままにしてある。

#!/bin/bash -eux

ovs-vsctl --may-exist add-br flets_v6
ovs-ofctl add-flow flets_v6 cookie=0x100000001,priority=10,action=drop

ip link add veth_flets_br type veth peer name veth_flets_ovs || true
ip link set veth_flets_br master br_flets
ovs-vsctl --may-exist add-port flets_v6 veth_flets_ovs

ip link add veth_homel_br type veth peer name veth_homel_ovs || true
ip link set veth_homel_br master br_homelan
ovs-vsctl --may-exist add-port flets_v6 veth_homel_ovs

ovs-ofctl del-flows flets_v6 cookie=0x100000002/-1

ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=320,ipv6,ct_state=+trk+est,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=310,ipv6,ct_state=-trk,action="ct(table=0)"
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=310,ipv6,ct_state=+trk+new,in_port=veth_flets_ovs,action=drop
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=300,ipv6,ct_state=+trk+new,in_port=veth_homel_ovs,action="ct(commit),normal"
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=1,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=2,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=3,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=4,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=133,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=134,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=135,action=normal
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=100,ipv6,icmp6,icmp_type=136,action=normal

ip link set veth_flets_br up
ip link set veth_flets_ovs up
ip link set veth_homel_br up
ip link set veth_homel_ovs up

Open vSwitch と ConnTrack を連携させることにより、基本的に中から外の通信でのみ established 扱いとして通信できるようにしているが、 RA 含め、いくつか通信やアドレス設定に必須と考えられる項目を追加している。この辺りは精査が必要かもしれない、

なお、この後も含め、 priority の指定を昔の BASIC の行番号みたいな感じにしているが、削除して整理できるので一応連番でつけても支障がないはずではある。

LAN 内 DNS サーバー(Unbound)を IPv6 で待ち受ける

基本的には LISTEN はさせるだけではあるが、今回だと IPv6 アドレスは可変で、自分で決定できないという問題がある。そのため、

  • unbound の設定ファイルには、 LISTEN するインターフェイス名の指定はIPアドレス指定はあったが、インターフェイス名指定が見当たらなかった
  • 仮に DNS 自体の LISTEN がうまくいったとしても、それを LAN 内に広報する必要がある

という問題がある。設定ファイルの動的生成はできればやりたくないので、 unique local address (Unique local address – Wikipedia)で LISTEN させて、 LAN 内からのみ到達できるようにする。

なお unique local address はインターネットに流すことができないIPアドレスで、 fd00::/8 のレンジのものだが、どうやら RFC4193 ではなぜか、 fd に続く40ビット(Global ID)を RANDOM な値にするようになっており、 sequentially に割り当てたり well-known な数字を使うことはなぜか MUST NOT になっているようだ。ローカルに閉じている以上 Global ID は自分で管理できていれば支障はない想定だが、真面目にやるなら40ビットの乱数を生成して fd の後に結合することでIPアドレスの先頭48ビットを決定することが望ましいかもしれない。

というわけで netplan の yaml を編集してこのアドレスを持つブリッジを作る。Open vSwitch 経由でしか受け付けないので子は netplan の時点ではいない。また、戻りのパケットを通すため、普通の方法で RA しているブリッジも用意する。

network:
  bridge:
    ...
    br_my_v6:
      interfaces: []
      accept-ra: true
    br_v6dns_l:
      interfaces: []
      accept-ra: false
      addresses:
      - fdXX:XXXX:XXXX:1::1/64

Unbound はこの方法なら普通に LISTEN を追加できる。

    interface: fdXX:XXXX:XXXX:1::1
    access-control: 2400::/8 allow

Open vSwitch でいい感じにリクエストのパケットを誘導する。レスポンスは br_my_v6 の方から出ていくので action:normal にしておけば勝手に帰る。

#!/bin/bash -eux

ip link add veth_my_v6_br type veth peer name veth_my_v6_ovs || true
ip link set veth_my_v6_br master br_my_v6
ovs-vsctl --may-exist add-port flets_v6 veth_my_v6_ovs

ip link add veth_v6dns_br type veth peer name veth_v6dns_ovs || true
ip link set dev veth_v6dns_br address 0e:00:01:00:00:01
ip link set veth_v6dns_br master br_v6dns_l
ovs-vsctl --may-exist add-port flets_v6 veth_v6dns_ovs

ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=410,ipv6,ct_state=+trk,in_port=veth_homel_ovs,ipv6_dst=fdXX:XXXX:XXXX:1::1,action=mod_dl_dst:0e:00:01:00:00:01,output:veth_v6dns_ovs
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=410,ipv6,ct_state=+trk,in_port=veth_my_v6_ovs,ipv6_dst=fdXX:XXXX:XXXX:1::1,action=mod_dl_dst:0e:00:01:00:00:01,output:veth_v6dns_ovs
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=400,ipv6,ct_state=+trk,ipv6_dst=fdXX:XXXX:XXXX:1::/64,action=drop

ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=300,ipv6,ct_state=+trk+new,in_port=veth_my_v6_ovs,action="ct(commit),normal"
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=300,ipv6,ct_state=+trk+new,in_port=veth_v6dns_ovs,action="ct(commit),normal"

DHCPv6 をいい感じにして DNS サーバーを広報する

ここまでの時点で、 RA と別に DHCPv6 が降ってきてはいたのだが、クライアントに到達していなかった(Android ではそもそも DHCPv6 リクエストが行われなかった)。おそらく、行きがマルチキャストで帰りが src もユニキャストだった影響で、 ConnTrack で行きと帰りが別々の接続とみなされたからだと思う。幸いにして少なくとも現時点では RA には DNS サーバーの情報は含まれず、 IPv4 で使っていた DNS サーバーがそのまま使われているようだ。LAN 内の DNS サーバーでないと LAN 内のサーバーの IP アドレスがわからないのでそれ自体は望ましくはあるが、整理しておくのと、一応 IPv6 シングルスタックでも DNS が通るようにしておく。

なお、次のような DHCPv6 の情報が降ってきていた。軽くウェブ検索した限りだと、フレッツではまあまあ使われているサーバーのようには見える。

(DNS-server 2404:1a8:7f01:b::3 2404:1a8:7f01:a::3) (DNS-search-list flets-east.jp. iptvf.jp.)

現時点で、 RA は正しく到達し、 DHCPv6 を使用するようなフラグがセットされているようだ(次の出力は tcpdump を br_flets 側で取得し、 tcpdump で -tnlvv に相当するオプションもつけたもの)。

        hop limit 64, Flags [other stateful], pref medium, router lifetime 1800s, reachable time 300000ms, retrans timer 10000ms

そして、 DHCPv6 の問い合わせも(行うよう実装・構成されている OS では)行われるが、結果パケットが弾かれて到達しないので使用できない。DHCPv6 は RA の送出元に送るわけではないようで、再度マルチキャストで行われるようだ。

なので、自前で DHCPv6 を受け付けて DNS サーバーだけを正しく返すことにする。

Linux の Network Namespace と radvd / dnsmasq で IPv6 SLAAC (+RDNSS) を試す – CUBE SUGAR CONTAINERの「dnsmasq (RA + Stateless DHCPv6)」では、 dnsmasq で RA と stateless DHCPv6 の両方を行っている。ただし、今回は dnsmasq はこの /64 ネットワークの実際のゲートウェイとは別のマシンで動かすので、 RA を行ってしまうと疎通しなくなる。dhcp-range=:: についての説明を man dnsmasq で読んでみても、 DHCPv6 を活かしながら RA を無効にする方法は見つけられなかった(見落としただけであるかもしれないが)。なので、この方法をベースに、 dnsmasq からの RA は Open vSwitch で叩き落すことにする。

#!/bin/bash -eux

ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=420,ipv6,ct_state=+trk,in_port=veth_my_v6_ovs,icmp6,icmp_type=134,action=drop
ovs-ofctl add-flow flets_v6 cookie=0x100000002,priority=420,ipv6,ct_state=+trk,in_port=veth_flets_ovs,icmp6,tp_src=547,action=drop

ついでに、 flets 側からの DHCPv6 は叩き落しておくことにした。

dnsmasq 設定ファイル

interface=br_my_v6  # 既に listen-address がいても追記可能だった

dhcp-range=::,constructor:br_my_v6,ra-stateless
dhcp-option=option6:dns-server,[fdXX:XXXX:XXXX:1::1]

ただし、この方法では、将来フレッツが RA に RDNSS を付すようにしたときには、宅内の DNS サーバーでないと参照できない接続先へのアクセスでは困りそうだ。

IPv6 テストサイトの結果

The KAME project のウェブページでは、無事に踊っている亀を見ることができた。

https://ipv6-test.com/ では、次のようになった。

簡易速度測定

Ubuntu 21.04 のラップトップで、 IPv4 を無効にして IPv6 のみで今回で構成したネットワークに接続したところ、正しく DNS も解決され、 https://www.google.co.jp/ や https://nhiroki.net/ などの IPv6 接続が可能なウェブページには接続できた。また、その環境で、 Google 検索で “speedtest” と検索して出てきた Google 組み込み(Measurement Lab と提携とのこと)のスピードテストでは、次のような結果になった。

なので、ギガビットのインターネットの想定であれば、 Celeron 3855U + Ubuntu + Open vSwitch で IPv6 のブリッジ(嘘)を実現するのは、性能上は妥当だと言えそうだ。

ISUCON 11 予選に参加した

ISUCON 11 の予選にいつものチーム「ワイハリマ」で参加していた。利用していたリポジトリは https://github.com/yuta1024/isucon11 で、予選終了後に public になっている。最終スコアは 26656、走らせたベンチマークの中で最良のものは 29189。記事執筆時点では最終結果は発表されていないが、再起動後に機能を維持すること自体は確認している。(追記:ベンチマーク周りの事情で追試の際に再計測が行われ、最終スコアは 27163 だった。)

COVID-19 による緊急事態宣言の最中であり、いくらワクチン接種済みとはいってもさすがにチームメンバーも物理的には集合せず、各自自宅からオンラインで参加。

時間としては 10:00-18:00 の8時間の予定で、定刻通り10時開始、17:20頃にポータルに問題があってベンチマークが行えなくなり18:10頃に復旧、競技は18:45まで延長となった。

いつものように言語を PHP に切り替え、ベンチマークを流して kataribe で様子を見たり実装を見たりしながら改善方針を検討した。デフォルトの Go の初期実装で走らせると 1656 だったのに対し PHP に切り替えた直後は 980、初期実装の段階では Go に負けている。PHP は(ISUCON ではいつものことのようだが) Slim が採用されており、しかし ./app/routes.php にほぼ全てが書かれている形のようだ。そして、とにかく isucondition テーブルへの書き込みに該当するリクエストの件数が多いようだ(ただし初期実装では9割のリクエストが PHP 側で無視されている。書き込んだ変更が反映されて初めて点数ではあるものの書き込みが無視されること自体のペナルティはなし)。

isucondition のテーブルで3つの値の true, false をわざわざ CSV 形式のテキストで保存しているのを発見し、 TINYINT 3 つのカラムに切り替える改修を行った。ただ、これは明確に効いたという感じではないようだ。今にして思えば結局このカラムは3カラムまとめて取得してくるものでデータベース側でどうこうするものではないので、実運用上するならともかく、速度だけ気にするなら特に影響が大きいというものではなかったのだと思う。

assets の静的ファイルがなぜか PHP の file_get_contents で実装されているという問題があり、これはメンバーが nginx で返すように修正していた。

ここまで一台のサーバーで三人が作業していたが、3 VM 同じものが動いているので、ここで3人がそれぞれ1台の VM を使うようになった。

とにかく POST /api/condition/<uuid> へのアクセスが非常に多く、無視している9割も一度 PHP で処理しているのはシンプルに問題がある。また、少なくとも ISUCON での経験上は Slim のオーバーヘッドは無視できないほど重くなることもある印象だった。ISUCON の PHP 実装は Slim で書かれており、かつほぼすべてを ./app/routes.php に書いていて Slim のメリットを生かせていないことが多く、もともと PHP が初期実装として有利でない現状では初期実装としてもフレームワークなしのべた書きにした方がバランスがとれるのではとは思うものの、Slim のメリットを活かしたコードになっていないなら逆に改修箇所も小さいので、要所を絞って一つ、二つ脱却するだけなら現実的にコンテストの時間内で行える。そのため、残りの1割でも充分リクエスト数が多いこの API は Slim を脱却したいという話になった。90% の割合は PHP 側で何もしないで処理しているはずにも関わらず、1割を超えるリクエストが nginx のログで status 499、つまり実際にはレスポンスすら返却せずタイムアウトしているのも気にはなっていた。

メンバーが nginx 側のロードバランシング機能を用いて一定の割合を nginx 側で所定の形式で返却、残りのみを PHP に回す処理を書いていて(これも冒頭にリンクを張ったリポジトリに上がっている)、これはかなり効いたようだ。さらに初期実装で 90% を無視していた割合を見直して調査した結果、そもそもこの時点では 90% でも捌き切れておらず、さらに高い値にしてある程度捌けるようにした方がスコアが伸びることが判明した。GitHub のこの時のプルリクエストでは 98% を無視する(2% しか php-fpm に流さない)ことになっていたが、この値は定期的に見直しているのでどの時点での値かは正確には思い出せない。

僕は POST /api/condition/<uuid> を Slim から脱却し単一 PHP ファイルで動作するようにする修正を行っていた。これはさらに別 VM へのオフロードもこの時点で目論んでいた。なお、この API はざっと見た限りでは WebUI からは行われずベンチマークからしか修正の正しさを確認できないのがやや面倒ではあったが、ベンチマーク自体はシステムがスムーズに動作していたので大きな問題でもなかった。

また同時に PHP の xdebug モジュールの無効化やデータベースのスロークエリを確認した上でのインデックス追加を行っているメンバーもいた。

この時点で15時過ぎ、10058 のスコアを記録した。

ここから先は3台の VM の役割を検討し始めた。まず、 php-fpm も mysql も nginx も全て CPU をまあまあ使っていたので、 MySQL は別 VM への移行を決定。更に POST /api/condition/<uuid> も別 VM に渡すことになった。ここで3台の VM の役割がわかれるが、リポジトリに置いていたファイルはあくまで全台に撒いても問題ないように書いていた。少なくとも今回は全台共通で撒けるようにする工夫に大きな手間はかかっていなかったと思うし、余計なことを考えないで良い効果の方が大きかったと思う。

多分この頃、時刻で言えば17時頃に、再起動試験も行った。とはいってもデータの置き場所は結局変更していないので、データ保持の確認は行わず機能維持のみを確認した。ミドルウェア周りはいうほどいじっていないしオンメモリ化もしていないので、特に問題なし。心配する要素がないのでデーモンが上がってくることのみのチェック。ただし無効化したつもりの余計なデーモンが起動してくるということはあった。

この後は、いくつかの重い処理をどの VM にやらせるかを移動したり POST /api/condition/<uuid> の無視割合を変更したりしてはベンチマークを走らせるといったことを3人でしていた。またこのころ、 nginx の client_body_buffer_size の調整や、 InnoDB 周りを主とした MySQL 周りのチューニング、 nginx の各種パラメータの修正をしていたメンバーもいたようだ。

なお、認証が必要な API は単に別のサーバーに逃がすだけだと 401 Unauthorized を返して正常に動作しなかった。調べた結果、 Slim 標準のセッション管理機構がサーバーローカルで閉じていて複数サーバーにまたがるとセッション管理が機能しないことが原因のようだった。ルールにはこういうサーバ側で生成する文字列は文字種を変えなければ自由にして良いと書いてあったと記憶していたこと、 Cookie に関するルールは記憶の限りではなかったこと、そもそも言語標準のセッション管理機構を用いるのであれば初期実装が複数言語あるアプリケーションで共通の何かを守らなければ外部連携に関するトラブルが発生するとは考えづらいこともあり、 Cookie に直接ユーザー名を保存して認証はユーザーの申告を全面的に信用するという方針に実装を変更した。これにより任意の API について php-fpm の負荷はどのサーバーにでも逃がすことができるようになった。

この調整を行い、分散具合や負荷のかかり具合としては良い感じになったと感じた。

この後も無視割合を変更する調整を行った。最終的に、有効1:無視5、つまり無視割合 83% 相当にすることを決定、ログなどの性能分析には有益だが直接的には不利にはなっても有利にはなりえないオプションを無効化し、その状態でベンチマークを二回ほど走らせたら一応は他の設定よりは良さそうな値にはなり、残時間もあまりなかったのでここで作業終了。

後から改めてコードを見てみると、 kataribe 報告で合計時間が多いとされていた API のいくつかには、 isu それぞれについて isu_condition テーブルから最新の情報を取得するクエリが実行されており、これが HTTP リクエスト 1 回に対して多数回呼び出されるものがあった。isu それぞれについての最新情報は isu_condition の時に併せて更新すれば isu テーブルに持たせられることから改善の余地があったと考えられ、今回は実装の修正をおろそかにしていたことが悔やまれる。

コンテストとしては、まず問題としてはプログラム自体の改善と3台の VM の活用それぞれがバランス良く織り込まれていたし、改善やその調査は ISUCON 専用の何かというものが多いわけでもなく実用的な技術の延長上にあるものが多かったので、質が高かったと感じる。また、今回は終了直前にポータルが50分ほど落ちた以外には運営側起因のトラブルは(少なくともワイハリマに影響するものは)発生しなかったし、ベンチマークボタンを押したらキュー待ちもほとんどなくほぼ即時にベンチマークが実行された。ベンチマークの結果自体も同じプログラムで実施すれば複数回実施しても大幅な変動が出ないものであった。全体を通して、8時間の競技の枠内で最大限に高速化技術を発揮できるかに集中できるものになっていたと思う。

(追記)メンバーの write up:ISUCON11 Qual writeup – yuta1024’s diary

PiPO X9s (12V 版)に Ubuntu Desktop 20.04 をインストール

以前買ってきた PiPO X9s に Ubuntu Desktop 20.04 をインストールした。その際、デバイス周りで設定が色々必要だった。なお前回の記事にもある通り、 X9s と呼ばれるものでもマザーボードが新しくなっており、 X9s という名前でも全くデバイスの情報が出てくるのでウェブ上の情報では混乱することがあるようだ。今回は電源電圧が 12V のものである。

数十秒でスリープしてしまう現象

まず、 Ubuntu のインストーラーを起動した時点で、操作するしないに関わらず数十秒でスリープしてしまう現象が起きて、そのままでは操作がままならなかった。調べてみると、 /proc/acpi/button/lid/LID0/state というファイルに closed である旨が書かれていた。どうやら lid スイッチが見えており、これが closed として認識されているために、ラップトップのふたを閉じた時の動作が行われているようだった。

どうやら似たようなことが起こるデバイスは他にもあるようだ:razer-blade-stealth-linux/ubuntu-18-10.md at master · rolandguelle/razer-blade-stealth-linux に書かれていた。基本的には button.lid_init_state=open という起動オプションを渡すと良いようだ。インストーラや初回起動時は GRUB のメニューから手でこのオプションを追加し、インストール完了後に /etc/default/grub の GRUB_CMDLINE_LINUX_DEFAULT に button.lid_init_state=open を追加して、 update-grub で生成して解決した。

無線関係

WiFi については前回記事と同様、日本で合法に使用できないと思われる内蔵 WiFi を無効化して USB WiFi を使用することにした。Ubuntu 20.04 では GUI から WiFi を有効にする際、内蔵 WiFi と USB WiFi を別々に設定できたため、一応そのままでも USB WiFi のみを使用することができると思われるが、networking – How to disable WiFi interface with netplan? – Ask Ubuntu を参考に、以下の設定で無効にした。(wlan1 として見えていた時に wlan1 を無効したら、 wlan0 として見える時もあったので両方書いた)

$ cat /etc/network/interfaces
auto wlan0
iface wlan0 inet manual
        pre-up ip link wlan0 set down
        pre-down ip link wlan0 set down
        down ip link wlan0 set down

auto wlan1
iface wlan1 inet manual
        pre-up ip link wlan1 set down
        pre-down ip link wlan1 set down
        down ip link wlan1 set down

Bluetooth は使わないので普通に設定画面から Bluetooth を無効化した。

Sileadtouch

タッチパネルについては sileadtouch が使われている。Ubuntu 20.04 には silead というモジュールがデフォルトでインストールされていたが、機器ごとに固有となるファームウェアを /lib/firmware/silead/mssl1680.fw に配置する必要があるようだ。

ファームウェアについては onitake/gsl-firmware: Firmware repository for Silead touchscreen controllers の tools/scanwindrv で SileadTouch.sys を読み込ませて出力されたものを利用した。SileadTouch.sys は最初にインストールされていたものだが、一応 PiPO のウェブページからリンクされている Baidu のクラウドストレージに置かれたイメージファイルからも復元はできる。なお、解像度などの設定が書き込まれたファームウェアを作る fwtool は標準の silead モジュールとは別のドライバを使う時のもののようだ。NANOTE P8でLinuxを動かすメモ – 信頼できる発行元を参照した。

firmwre_00.fw を /lib/firmware/silead/mssl1680.fw に配置した後、 modprobe -r silead して modprobe silead すれば、反応はするようになった。ただし、先ほどの NANOTE P8 の記事と同様、この状態ではタッチ位置が大きくずれる(タッチパネル全体の認識が画面の左上の矩形領域にマッピングされているようだった)ので、キャリブレーションを行う必要があった。キャリブレーションには xinput-calibrator パッケージ(universe リポジトリにあり、 Live USB では apt-add-repository universe する必要があった)の xinput_calibrator を用いるが、そのままだと設定ファイルを書いてもキャリブレーションが反映されなかった。なお、 xinput_calibrator は DPI を 150% にした状態では正しく動かなかったが、 100% の状態で設定したパラメータを 150% の状態で使っても問題はなかった。

一時的な反映については、マルチディスプレイ環境での液晶タブの調整 – yu_izumi’s blogに記載されている方法で行列に変換し、 Coordinate Transformation Matrix の形式に変換するととりあえず意図したとおりのところがタップされるようにはなった(ただしこのページの数式の係数の 0.5 は今回は不要だった)。minX, maxX といったパラメーターが、実際の広大な入力の値域のうちどこからどこまでが実際の画面であるかのパラメーターとみなして、元のキャリブレーションパラメータで導出される画面上の座標を新しいキャリブレーションパラメータで導出されるはずの座標に変換する行列を作るイメージ。

恒久的な反映としては、 xorg – Touchscreen calibration fails on Ubuntu 20.04 / PixelBook – Ask Ubuntuに書かれているように、 xserver-xorg-input-libinput を削除して xserver-xorg-input-evdev をインストールし、再起動してから xinput_calibrator を実行すると xinput_calibrator の出力形式が変わるようだ。 evdev の方を入れた状態で出力したファイルを /usr/share/X11/xorg.conf.d/99-calibration.conf に保存して再起動すると、正しく認識されるようになった。

ただし、これでもスリープ後等のロック画面などでは正常にタッチパネルが使えていないように見える。

(追記)Firefox では XINPUT2 の指定有無に関わらずあまりきちんと認識してくれないように見える。chromium そのまま問題なく認識してくれそうだ。

スクリーンキーボード

おそらくハードウェア固有の話ではないが、タッチパネルのみのデバイスでキーボードを繋がず運用したいときに必要になるスクリーンキーボードについて、 Ubuntu の設定の Universal Access のところから Screen Keyboard を有効にできるが、ファンクションキーどころかカーソルキーも見当たらず、それらを有効にする方法も見当たらなかった。

ウェブで調べてみた限りだと、以前のバージョンの Ubuntu でデフォルトだった OnBoard をインストールしてそちらを使っている人もいるようだ。こちらはこちらでテキスト入力画面で自動で起動する設定が試した限りだと動作しないが、キーが足りないよりは良いので一旦は OnBoard を使ってみることにした。

(追記)Ubuntu 20.04 + GNOME で、物理キーボードなしでタッチスクリーンを用いた場合、 OnBoard 使用中でも GNOME 標準のスクリーンキーボードが出てくるようだ。

その他

バッテリー残量が見えているが、 PC 自体を動かすためのバッテリーは搭載していないと思われるので何の数字か謎。84% などといった数字が見えており放置しても変化しないが、再起動すると変化することもある。(配送時の航空関係の申告はリチウム金属電池であってリチウムイオンではなかったし、 AC アダプタへの電源供給を止めると一瞬で電源が落ちる。また、 Windows 10 からはバッテリは見えなかったと記憶している。問い合わせで仕様を確認した際に no battery かと聞いたら 40mah と言っていたが、この理由からボタン電池ではないかと思っている。)

その他、試した限りだと microSD カードリーダー、 USB ポート、音量ボタン、電源ボタン、内蔵スピーカー、イヤホン端子は正常に認識されて動作しているようだ。手前の Windows ロゴ(Win10 では使えていた)は、この方法でセットアップした後にタッチしても何も起こらず、 xev でも何の反応もないようだ。

スリープ・復帰もそれ自体は正常に動作しているように見える。ただし、タッチパネルのところで言及した通り、スリープした際にロックしているようになっているとログイン画面では正常にタッチパネルが使えない。スリープしてもロックしないように設定すれば問題ないようだ。ログイン画面についてはキーボードを使ってもうまくログインできないようだったが、そもそもロックが必要な使い方をしない想定なので、自動ログイン ON、スリープ時にもロックされないようにしてそれ以上は追わないことにした。

挙動については  RAM4GB で Windows ほど何をするのも重いということにはならなかったが、 Firefox で試した限りでは YouTube の 1080p 動画は厳しいようだ。

(追記:そもそも存在を完全に忘れいていたが、 Win10 で使えていた加速度センサーは Ubuntu でも認識されているらしく、本体の向きを変えると画面の向きも変わるようだ。ただし、横にしたときの画面回転は想定通り[誰の?]動いているようには見えない。いずれにしてもタブレットパーツの流用の都合で設置されていると思わしきセンサーであって、少なくとも僕にとっては必要なセンサーではないので、正しく動作させるための検証をするつもりはない。)

PiPO X9s を買ってきた

持ち運びを想定していない、机に置く用のデスクトップタブレット端末である PiPO X9s (4+64GB 版)を買ってきた。

まず PiPO の端末の売り場はあちこちに色々あるが、どれがちゃんとしたところかよくわからなかったのでそこからというところだった。Baidu で「pipo」と検索すると、 http://www.pipo.cn/ が公式マーク的なものがついていたので、一旦ここから辿っていくことにした。(結果的に届いた商品のパッケージにも www.pipo.cn の記述があった)

今回はこのサイトの英語版の Contact Us ページに書いてある alibaba.com のリンクである https://pipotech.en.alibaba.com/ から購入した。なお Contact Us のところには公式のように見えるが公式ではないサイトが一つ注意喚起されている。

alibaba.com の会員登録にあたっては、 alibaba.com の上部の “Join Free” からだと会社名の入力が必須(しかもおそらく日本の不動産契約の書類に書くような個人が所属する会社としての記述というより、会社のアカウントとしての立ち位置だと思われる)になっていた。先に商品を選んで Contact Supplier を押し、そのタイミングで出てくるログイン画面から Join Alibaba.com を選択すると “I’m a non-business entity” というチェックボックスが利用可能になり、個人でのアカウントが登録可能になった。ここでチャットでやりとりし、在庫がなかったので在庫が復活するのを待って購入することができた。本体 179USD, 送料 50USD で合計 229USD で、香港から FedEx で送られてきた。(追記:後日FedEx から関税を支払うよう郵便で通知された。「その他税金(Consumption Tax/VAT)」が1000円、「関税その他税金についての特別取扱手数料(Duty Handling Fee)」が1000円で合計2000円だった。)

PiPO X9s と呼ばれる製品は過去に何度か X9s という名前のまま設計を変えているようだ。提供されている OS イメージやウェブ上の資料を見る限りだと少なくとも、

  • Windows 10 + Android デュアルブートモデル (OS イメージがある)
  • Windows 10 シングルブートモデル、電源電圧5V版(OS イメージがある)
  • Windows 10 シングルブートモデル、電源電圧12V版(新マザーボード版で、おそらく今回買ったものはこれ。 OS イメージ

が存在しそうに見える。その中でさらにバリエーションがありそうにも見える。

特に今回買った 12V 新マザーボード版は、ざっくり調べたところおそらくだが

  • 電源電圧(今回買ったもの:12V、旧版:5V (PIPO の X9s 紹介ページでも 2021/5/23 現在 5V ということになっている)。ドライバダウンロードも電圧で区別している)
  • タッチパネルコントローラー(今回買ったもの: Sileadtouch、旧版:Freaktab のやり取りを見るにおそらく Goodix)
  • メインボード(イメージダウンロードページのところに「注意此系统支持新主板,机器电源为12V!」と注意があるので、おそらく 12V 版はメインボードが変わっている)
  • 画面解像度(今回買ったもの:1920×1280、旧版:おそらく 1920×1200 (PIPO の X9s 紹介ページでも 2021/5/23 現在 1920×1200 ということになっている。Android 版のイメージのファイル名にも 1920×1200 を示唆する文字列がある)
  • UEFI (今回買ったものは NTFS パーティションからのブート不可、旧版と思われるイメージの説明書や、FreakTab でのやり取りを見るに、おそらく旧版は NTFS ブート可と思われる)

辺りが異なっていそうに見える。X9s という名前がついていても、見た目やできることは似ていても中身は旧版と別物と思った方が良さそう。従ってトラブルの際はウェブ検索で調べて X9s についての対処が書いてあっても、当てはまるかどうかわからない。

今回買ったものは負荷をかけてもファンの音はしていないようだ。

購入後、ドライバを

dism /online /driver-export /destination:C:\driver-bak

でバックアップして Windows 10 をクリーンインストールし、デバイスマネージャーからバックアップしたドライバを適用しようとしたが、いくつか挙動に問題があった。試行錯誤の結果、以下の感じでクリーンインストールした状態から、見た限り正常に動作するようになった。Sileadtouch はウェブ検索してみると中国製の格安タブレットに多く使われているようで、やや鬼門であるようには思われたが、結局のところはバックアップしたドライバか、 PIPO が公開しているイメージファイルで正常動作させられるようだ。

  • (そのままで当てられるドライバを先に当てたが、このタイミングでやらなくても良いと思われる)
  • “bcdedit -s testsigning on” で、未署名ドライバのインストールを許可
  • ウェブで調べた方法で詳細は覚えていないが、回復メニューから再起動を行い、ドライバの署名を強制しないモードで起動
  • デバイスマネージャーで不明なデバイスになっているデバイスを片っ端からドライバ更新を指定し、バックアップしたドライバのフォルダを指定してインストール
    • Sileadtouch について試行錯誤の結果、dism でバックアップしたものではなく別途公開されている OS イメージに含まれていたものにしたが、中身は同じと思われる
  • この時点で、なぜかタッチパネルが、短く押しても長押し判定されるようになっていた。もとのインストールイメージでも sileadtouch.fw というファイルがあるわけではなく、理由がなかなかわからなかった。
  • “bcdedit -s testsigning off” してテストモードを抜けて再起動すると、タッチパネルが誤って長押し判定されることがなく、正常動作した。理由はわからないが一旦これで進めることにした。
  • スタンバイや電源オフにしても液晶のバックライトが点灯したままになっていた。デバイスマネージャーで Microsoft Basic Display Adapter と表示されていたデバイスについて、 dism でバックアップしたドライバからの更新を試みると、数分経った後に Intel(R) HD Graphics として認識され、スタンバイなどの際はきちんと消灯するようになった。

Intel Atom x5-Z8350, RAM 4GB というスペックなので、 Windows 10 が動くとは言っても速くはないし、今時だとできることは限られている。この辺りはうまくカバーする使い方をする必要があるだろう。

また、液晶と実際に触れる面の間にまあまあの厚さのガラスがあるのか、斜めからタッチすると視差が出るようだ。形状・用途的には斜めからタッチすることも多いと思うが、慣れるしかない。

ちなみに Ubuntu については USB ブートで試してみた限りで、放置すると1分程度でスリープしたり電源が落ちたりする現象があったのと、タッチパネルが正常に使えなかった。ウェブの情報を見るに Silead のタッチパネルを Linux で動かしている人はいるようだが、おそらく Ubuntu そのままというわけにはいかないのと、1分程度でスリープする現象についてはまだ追っていないので、入れようとすると苦労しそうだ。(追記:結局 Ubuntu はインストールできた。PiPO X9s (12V 版)に Ubuntu Desktop 20.04 をインストール参照。)

なお、技術基準適合証明やそれに類する記述は見当たらなかったし、そもそも日本市場を意識しているとも思えないので、おそらく内蔵の無線は日本では合法には使えないと考えて無効化した。内蔵の有線 LAN で困らない場合もあると思うが、今回は小さく安い USB WiFi を背面ポートに刺すことにした。特に邪魔にはならない(実運用上は、今回使わないアンテナや、 AC アダプタの端子の陰に埋もれる)と思われる。

WiFi アダプタを刺した様子

PIPO X9s 背面に ELECOM WDC-150SU2MBK を刺して上から見たところ

(追記)書き忘れていたので追記。PC として機能させられるだけのバッテリーは搭載しておらず、AC アダプタを抜くとすぐに電源が落ちる。輸送時には UN3091 のラベルが貼り付けられ、チャットではバッテリーについて確認した際にバッテリーなしではなく 40mAh と言っていたので、おそらくこれがボタン電池かそれに近いものを刺していると思われる(が、分解するためのねじ穴にアクセスするには接着されたゴム足を外す必要がありそうなので、分解しての確認はしていない)。少なくとも UN3091 はリチウムイオンバッテリーではないので、品質の低いリチウムイオン電池が問題を起こす心配はなさそう。

千葉県の福岡の桜並木に行ってきた

桜が満開になる季節になってきたので、千葉県にある福岡の桜並木(リンク:千葉県観光物産協会による紹介サイト)に一昨日ドライブに行ってきた。道沿いに断続的に桜が咲いていてなかなか良い感じだった。

「福岡の桜並木」では使った車のカーナビに登録されておらず、鹿野山神野寺への道として設定しても周辺の道としては見当たらなかったが、結局は近くのコンビニで車を止めている時に Google マップで「福岡の桜並木」検索したところ見つかった。鹿野山 福岡口(リンク先は Google マップ)の T 字路から県道163号を西に進めばよいようだ。上の方に分岐があって片方は時間帯指定で進入禁止、かつ進入禁止の道もそうでない道もすれ違いができない道に見えたので、そこで引き返してきた。

下りの動画(ダウンロード

入り口

福岡桜並木入り口

反対側から(帰り)

中ほど(下り)

福岡桜並木中ほど

(投稿してから思い出したので追記)しかしこの日は渋滞が激しかった。カーナビはとにかく首都高を避けようとしていたし、実際のところアクアラインでは以下のような表示が出ていたのでカーナビに従って、浮島から湾岸線に出ず、殿町から出てあとは下道で帰ってきた。
2021/3/26 渋滞(湾岸環八から辰巳まで渋滞15km70分)

このブログを(多分) The Privacy Sandbox の FLoC による演算から opt-out した

最近、 Google が The Privacy Sandbox なる試みを行っているようだ。現在の広告その他のエコシステムを保ったままプライバシーを改善することを狙っているようで、 The Privacy Sandbox 自体が仕組みの名前というよりは複数の仕組みを作ることを通してプライバシーを改善するプロジェクトとして存在しているようだ。とはいえ、やはりサードパーティー Cookie を廃止するための代替としての、ブラウザ側でユーザーの閲覧履歴の分析・学習を行って、嗜好を知りたいサイト側にはどのグループ(Cohort)に属しているかだけを通知する FLoC が一番取り沙汰されているように感じる。

調べてみた感じでは現在のところ、広告のための学習のためにプライベートな情報を使う機構を今からわざわざ追加するモチベーションがあるとは思えない Firefox や Safari をどうするつもりなのかといった情報は見つけられなかった。もし少しでも Chrome 以外のブラウザを排斥する意図があるのであれば、それは Google によるインターネットの支配であり、オープンなインターネットの終焉であり、同一の事業者の広告が貼ってある複数ページの閲覧履歴を束ねて追跡されることよりもずっと危険な問題であると思うが、情報がない以上何とも言えない。

また、一応は学習を担当するブラウザベンダと学習結果を実際に評価できる広告事業者が別々の役割として存在しているわけで、何をどうやって学習するのかもよくわかっていないが、そのあたりは追っていない。加工されているとはいえ FLoC システム全体の学習のために、閲覧履歴をもとにしたデータをブラウザベンダとやりとりするというのであれば、ブラウザベンダが持つ権限は今よりずっと大きなものになりそうだと思っている。

ただし、少なくとも現時点で、明示的にサイト側で対応しなければすべてのサイトを学習の対象にしようとしているようだった。www.chromium.org ドメインにある The Privacy Sandbox のページからリンクされている GitHub リポジトリで WICG/floc: FLoC というものがあり、そこに現時点では以下のように記されている:

A site should be able to declare that it does not want to be included in the user’s list of sites for cohort calculation. This can be accomplished via a new interest-cohort permissions policy. This policy will be default allow.

opt-in 形式にした方が良いというプルリクは上がっており、まだ close はされていないようだ: Opt in cohort training by dmarti · Pull Request #42 · WICG/floc

従来では一応、関連の広告事業者の広告を表示する(といっても各社、広告にはなぜか自社ロゴではなくわざわざ似たような (i) のロゴを設置していて、マウスオーバーしない限りどこの事業者の広告かはわからないことが多いが)か、表示しなくとも関連の広告事業者に追跡させるためのコードを明示的に書いたページのみが対象になっていた。従って opt-out しなければ学習の対象になるというのは、従来と比べると随分学習に使うデータを乱暴に増やそうとしているように感じる。

opt-out するための方法は一応準備されている(まだ正式仕様ではないので、変わるかもしれないが)。

For example, a site can opt out of all FLoC cohort calculation by sending the HTTP response header:

Permissions-Policy: interest-cohort=()

従って関係ないサイトの広告のために閲覧履歴を使用されたくない場合は以下のようなヘッダを追加することになる。一応ブラウザから直接出る外部に値というわけではないものの、これらをもとに学習されたデータは任意のサイトから読みだされることになるし、学習の過程でブラウザベンダとの通信も発生すると思われることから、おそらくセンシティブな情報を扱うのであれば入れた方が良いだろう。

Permissions-Policy: interest-cohort=()

本質的にクライアント側の挙動であり、サイト側というよりはブラウザをインストールした側の責任ではある。とはいえ、プライバシーに寄せましたと主張しながら関係ないサイトの情報も広告に使うというのは、個人的には賛成できるものではない。そのため、せめてもの抵抗として、(ちゃんと排除できているか確認はできていないものの)このブログは interest-cohort は許可しないようにヘッダを追加した。これを理由に Google 検索の順位が下げらなければ良いが。

fontconfig の優先順位を調整して中国語フォントより日本語フォントを優先する

Ubuntu MATE 20.04 を使っていて、日本語で表示されるべきものがいわゆる中華フォントになってしまった(後述するがこれ自体は Ubuntu MATE のせいではなく自分の操作によるものだった)ので、フォントの優先順位を修正して日本語フォントを優先するようにしたのでメモ。

まず、フォントの優先順位そのものの設定は /etc/fonts/ で行われているようだった。調べてみると Linuxでアップデートしたら中華フォントになった件について(修正方法) – Qiita に、 Noto シリーズの JP のものを serif, sans-serif, monospace それぞれのエイリアスに追加する良い感じの fontconfig の xml があった。この記事では /etc/fonts/local.conf に置いていた。

これをもう少し管理しやすい場所に置きたかったので調べてみたところ、このファイルは /etc/fonts/local.conf ではなく $HOME/.config/fontconfig/fonts.conf に置くことで、管理者権限なしに自由に修正できるようだった。世代によって推奨される場所が変わったりはするようだが、そんなに特殊な置き場所というほどでもないようだ。

さらに、これは /etc/fonts/conf.d/50-user.conf によって呼び出されていた。この後で優先順位を変えて絞り込んだところ、/etc/fonts/conf.d/56-kubuntu-noto.conf というファイルで Noto 関係のフォントにエイリアスが指定されており、これより前に読み込めば、通常の漢字表示が日本語フォントになるようだった(先ほどの /etc/fonts/conf.d/56-aaa.conf にすれば日本語フォントになり、 /etc/fonts/conf.d/56-zzz.conf にすれば中国語フォントになった)。おそらく Kubuntu を追加でインストールしたときに変わってしまったのだろう。

その後に読み込まれるファイルを漁ってみたところ、 /etc/fonts/conf.d/64-language-selector-prefer.conf というファイルがあり、 Noto の中で、どの言語から順番に表示するかの優先順位が設定できるようになっていた。手元の環境だと JP のものが一番優先になっていたので、おそらく Kubuntu をインストールする前はこのファイルによって日本語フォントが優先されていたのだろう。

ということで結局この環境では

sudo ln -s /etc/fonts/conf.d/64-language-selector-prefer.conf /etc/fonts/conf.d/52-japanese.conf

としてこのファイルの優先度を上げることでほぼ解決できた(すべてのアプリケーションではない模様)。似たような問題が起きた時や他の理由でフォントの優先順位を変えたいときも、この辺りのファイルから見ていけば良さそう。

sshrc の内容に関するメモ: Xforward で必要

ssh で X 転送しようとすると次のようなエラーが出て困っていた。ぐぐってもよくわからなかった。

PuTTY X11 proxy: Unsupported authorisation protocol

原因は色々あるようだがググって出てきたものがなかなか当てはまらなかった。別サーバと比べたりしていたところ、 .ssh/rc を置いている場合、 /etc/ssh/sshrc が呼び出されないため、この内容がないと困ることがわかった。man sshd すると SSHRC の項に説明とサンプルファイルがあり、これが .ssh/rc を置いていることに阻害されていただけだった。.ssh/rc に man の内容に従って xauth 関連の記述を追記すると繋がった。

Win10 で使っていた SSD (Crucial MX200)の完全消去でハマった

Crucial MX200 SSD を ThinkPad W550s で、 Windows 10 Pro で使用していたが、新しい PC を買った後しばらく経ち、 Ubuntu MATE に入れ替えようと思ったため、一旦 SSD のデータを消去しようとした。その際、ハマったのでメモしておく。

まず、 SSD の secure erase はハードディスクと異なり、 SSD に対してコマンドを発行することで行えるようだ。それ自体の手順は Solid state drive/Memory cell clearing – ArchWiki に記されている通り、一度 hdparm でパスワードを設定した後、 hdparm で –security-erase オプションを使用すればよいようだ。frozen 状態に注意するよう注意書きがあった。SSD はハードディスクと異なり使用中の領域かどうかを管理しているようなので、性能上も security erase して使うのが一番良いように思えた。

しかし、今回の SSD では、これをしようとすると、エラーが出た。エラーの内容は記録するのを忘れてしまったが、コマンドの実行で何かバッファでもあふれているかのような、怪しいエラーメッセージだった。その時ウェブ検索していた内容から見るに、bad/missing sense data といったことが書いてあったようだ。この時開いていた、hard drive – hdparm error: SG_IO: bad/missing sense data – Super User の質問のエラーメッセージ(以下に引用)と同じようだ(数字の羅列が一致するかはわからない)が、状況は違うように思えた。

sda:  Issuing SECURITY_SET_PASS command, password="PASSWORD",
user=user, mode=high SG_IO: bad/missing sense data, sb[]:  70 00 05 00
なお先の ArchWiki で書かれている frozen かどうかは、それらしい行がこの時は見当たらなかった。そもそも、Security の項がなかった。

また、先頭 593MB は読み書きできるものの、それ以上は Input/output Error で読み書きできない(dd if=/dev/sda of=/dev/null bs=1048576 count=1000 で簡単に確認できた)現象にもなり、削除せずに使い続けることもできなかった。

色々調べた結果、忘却の彼方: Crucial M500/M550/MX100とSecure Erase に、 Windows 8 以降ではいくつかの SSD において、ディスクのセキュリティ機能を変更できなくなるような修正を加えており、 PSID を復旧すると元に戻る旨が書かれていた。特に今回は BitLocker を使っていた端末でもあり、暗号化された領域にロックがかかっていたりしても不思議ではないように思えた。

結局のところ、 PSID をリセットする操作が必要であり、これを行うと無事に他の操作も行えるようだ。Crucial の SSD であったので、 Crucial Storage Executiveをインストールしたパソコンに、 USB-SATA で当該 SSD を接続し、「PSID を元に戻す」を押すと、データが消去される旨が表示され、その後無事 SSD を利用することができた。hdparm -I でも Security: の項目に frozen かどうか、表示されるようになった。security erase も ArchWiki の手順で成功した。

ISUCON 10 予選に参加した

いつものようにチームワイハリマで ISUCON 10 予選に参加していた。今回はいつもにもまして手も足も出ない状態だった。

今回は競技に関するルールが事前に公開され、当日の競技開始と同時に ISUCON10 予選マニュアルが公開された。

今回は競技開始後もなかなかサーバに入れなかったり、ポータルが終始不安定でベンチマークも運が良い時しか走らせなかったりした。まずはざっくり画面を操作して機能を把握したうえで、ベンチマークが走るようになったら重いクエリがどこにあるかなど、徐々に詰めていった。また、サーバは 3 台与えられた。EPYC 1vCPU, RAM 1GB なのは良いとして、ディスクが 10GB なのが目に付いていた。

まずは目に付いたいくつかの重い検索クエリを軽くしようと、インデックスを色々張ってみたり、 SQL の外で実行しようとしたり、そもそもウェブアプリケーションの外で実行しようとしてみたりしたが、特段速くはならなかった。SQL が詰まっていたので専用サーバにしたことも、効果を発揮しなかった。recommend では条件が 6 つ OR で繋がっているのを、データの入力を工夫して一つにまとめてみたが、これもうまくいかなかった。

検索が重いことが分かっていたので、レプリケーションを行って検索系クエリを逃してみたが、却って遅くなった。3台のマシンすべてを使うことで CPU 使用率が 100% のマシンはなかったが、全体に CPU 負荷があまりかからなかった。レプリケーションを有効にするためにバイナリログを有効にしたが、これが問題になったようだ。後で考えてみれば、ディスク 10GB に目がついていたが、このディスクが低速で(今となってはわからないが、 SSD ではなく本当にディスクだったのだろうか)書き込みもそれなりに詰まっていた可能性がある。

また今回は、いくつかの最適化を試みた時から、互換性を失わないと思われる変更でベンチマークが通らなくなる現象にも苦しんだ。json_encode 周り及び、 PDO から SQL の結果を受け取る時に独自のクラスで受け取るもとの実装について、どこがしかの挙動を変えてしまったようだが、デバッグログの出し方がわからなかったこともありデバッグには終始時間がかかっていた。

もともと PHP 実装を選択した後一度も Go の初期実装スコアを超えていないことに加え、最終的にはベンチマークでの互換性テストすら通らない状態となり、 0 点となった。高速化視点での実力不足とは別にも、色々と反省すべき点を整理したほうが良さそうだ。