かみぽわーる

kamipo's blog

MySQL 8.0のクライアントでMySQL 5.7のサーバーに接続するとcharsetが設定されないかもしれない

methaneさんにMySQLのハンドシェイクパケットにはcollation_idが入ってることを教えてもらったので、本当にHandshake Response Packetからcharsetを設定しているのか調べてみた。

MySQL :: MySQL Internals Manual :: 14.2 Connection Phase

Handshake Response Packetのペイロードの構造を見ると先頭から8バイト目にたしかにcharacter_setのidを1バイト入れられるっぽい。

MySQL :: MySQL Internals Manual :: 14.2.5 Connection Phase Packets

MySQL 8.0のdefault collationのid一覧はこれ。

MySQL :: MySQL Internals Manual :: 14.1.4 Character Set

このパケットは、サーバーからのInitial Handshake Packetをパースしたあと、最初にレスポンスするときにmysql_fill_packet_headerで作られてサーバーに送られる。

https://github.com/mysql/mysql-server/blob/mysql-8.0.23/sql-common/client.cc#L4060

サーバーはparse_client_handshake_packetでクライアントからのレスポンスの先頭から8バイト目をcharset_codeとして取り出している。

https://github.com/mysql/mysql-server/blob/mysql-8.0.23/sql/auth/sql_authentication.cc#L2476

https://github.com/mysql/mysql-server/blob/mysql-8.0.23/sql/auth/sql_authentication.cc#L2581

最終的にthd_init_client_charsetで取り出したcs_numberから現在のスレッドハンドルのcharsetを設定している。

https://github.com/mysql/mysql-server/blob/mysql-8.0.23/sql/sql_connect.cc#L422-L423

これにより、mysql_options(mysql, MYSQL_SET_CHARSET_NAME, cs_name)してmysql_real_connect(mysql, ...)するとcs_nameのdefault collationがコネクションのcharsetとして設定されるわけですね。

ここで表題の "MySQL 8.0のクライアントでMySQL 5.7のサーバーに接続するとcharsetが設定されないかもしれない" についてなんですが、MySQL 8.0.1からutf8mb4のdefault collationがutf8mb4_general_ci (id: 45)からutf8mb4_0900_ai_ci (id: 255)に変更されたため、MySQL 8.0のクライアントがuff8mb4でサーバーに接続するとid: 255のcs_numberを送るけどMySQL 5.7はid: 255のcs_numberを知らないのでサーバー側のデフォルトの設定が採用されるという仕組み。

理想的なケースでは、サーバーに接続したらcharsetは適切に設定されるけど、最悪のケース、サーバーはMySQL 5.7でサーバー側のcharsetはutf8mb4に設定されておらずMySQL 8.0のクライアントからutf8mb4で接続するケースではコネクションのcharsetはutf8mb4に設定されない。

一応、接続後にSET NAMES utf8mb4すればサーバー側のutf8mb4のdefault collationが設定されるが、最悪のケースをカバーするために適切に設定してるひとには必要ない処理が増えて損をすることになるのでなんとか回避したい気持ちがあるけど、現状はそういう感じ。

新宿うまいカレー屋多すぎん?

いろいろあって自由な時間を活用してなんか人生が充実するようなことしたいなということで、ランチのおいしいお店を開拓しようというのをやっていた。その中でも新宿うまいカレー屋多すぎん?と思ったので行ったことのある新宿のカレー屋さんを紹介します。

三丁目と御苑前のあいだぐらいでちょっと遠いんだけど新宿でいちばん好きなカレー。🍆🍅🐔がうますぎるので🍆🍅🐔ばっかり食ってる。

  • 東京ドミニカ

草枕うますぎるけど遠いので、近場でうまいスープカレー食いたいときによく行く。

  • 魯珈

ランチタイムに限定35食ぐらいしか提供してなくて朝ノートに記帳して整番ゲットしないと食べられない貴重なお店。つぎいつ来れるかわからないのでこの日はルーとライスおかわりしました🍛

  • 半月

魯珈の近くにあるお店。整番ゲットしなくても入れてうまい。お店の名前の半月はお皿に盛ったルーが半月の形だかららしいです🌛

  • FISH

なんかどこからともなくすごいいい匂いしてくるから気になって調べたら人気店だった。激辛チキンがマジで激辛すぎて震えた:;(∩´﹏`∩);:

  • 極哩

野菜の彩りがオシャレで映え重視で来た。かわいい店員さんにおすすめされてグアバジュースも頼みました(ちょろい)

  • アチャカナ

ここで紹介した中だと唯一ライスかナンか選べるお店だったのでナンにしました🇮🇳

  • イエローカンパニー

めっちゃオーソドックスなスープカレーという感じ。カレーに合いそうなビールがけっこうある🍺

  • 酔っこら処

ほりさんのカレーおいしいです(^q^)

他にもおいしいお店あったら教えてください行ってみます🍛

2021年はブログを書くのをがんばろうという話

5ヶ月前に退職エントリを出してから、いろんな会社さんだったり個人的にだったり、いろんな人と話させてもらった。

blog.kamipo.net

もともと、Railsを改善する活動をずっとしてきて、僕は悲観的なところがあるので、Railsを改善することの価値は将来的には下がっていくだろうなと思っていて。なので、常にいまがRailsを改善する最も価値ある瞬間で、だからこそいまそれをやる意義が僕にはあって、いまやらないとその機会を失ってしまうだろうと思って、いまに至っている。

とはいえ、僕は価値があると思ってやっている活動もその継続性を考えると、経済的な価値に転換するポイントを見いださないと、僕の資金が続く限りはやります、資金が尽きたら終わりますになってしまうので、これまで"個人の趣味"としてやってきて改善すること以外はマジでどうでもいいと思ってそういうこと何も考えてこなかったから(ぜんぜんどうでもよくなかった)、継続性という点についてはこれからもいろんな人の意見だったりを聞いて考えていきたいところです。

まだコロナ禍になるまえ、オフラインでやってたころのESM, Inc.さん主催のOSSパッチ会の体験が僕にとってはとてもよく、Railsユースケースや改善点に関するフィードバックが得られて、それによって僕の活動が誰かにちゃんと届いてるという実感も得られる。

その体験をもっと広げられないかという思いもあって、いま、Railsのことに関するフィードバックだったり相談だったり(べつにRailsのことじゃなくてもいいし雑談とかでもいい)を受けられるように、SlackのGuestアカウントをもらってる会社さんが何社かある。

実際やってみると、あの体験を再現するのはなかなか難しいのだなと感じていて、そもそも、OSSパッチ会に来るような人はすでにOSSを改善しようって意識を持って集まってる人たちで、そんなみんな毎日「よーしOSSを改善するぞ〜!」みたいな感じで日々の業務をしているわけじゃないという、考えてみたらそらそうやなというのがまず最初に感じたこと。

あと、関係性的に業務のコードを見れるわけではないので、コード見たら「あ〜そういう感じか〜それはRails側で改善されてくれるとうれしい案件やな〜」ってわかりそうなことも、そういう感度をもった人が相談してくれない限り察知のしようがないというのも感じた。

たとえば前職でのケースでいうと、クソクエリでDBが死んでしまうのをMAX_EXECUTION_TIME()で対処したときに、これMySQL使ってたらマジで超絶有用な機能やしBasecamp, Shopify, GitHubMySQL使っとるねんからふつうにみんな必要やろってRails 6.0でOptimizer Hintsサポートを入れたり。

github.com

これ以前にもMAX_EXECUTION_TIME()使いたいねんけどどう思う?ってプエルトリコ人の同僚(プランテインが大好き)に相談されたときに、ええと思うけどクエリがタイムアウトしたときのハンドリングしたいよな〜ってことでStatementTimeoutエラークラス入れたり。

github.com

他にも僕はuniquenessバリデーターのcase_sensitive: false警察をやってたんですが、フィロソフィーのダンスのオタクの同僚(おとはす推し)に「Rails側でなんとかしてくださいよ〜」って言われて、既存アプリに影響ある変更をするのは気合い要るけどまあ気合いだけの問題なんでやるか〜ってことでuniquenessバリデーターの挙動変えたり。

github.com

他にもそういう感じのはいっぱいあって、こういう話はコードが見れない側からだとちょっと難しいなと感じた。

あとはまあ、「Railsコミッターだけど何か質問ある?(雑談でも可)」って人がいきなり現れても、話題ないよな、僕も自分から雑談するタイプじゃないし、というのも感じてる。

そんなこんなで、そういう状況を改善したく、ひとつには僕がいままで自分の活動だったり改善だったりを宣伝してこなかったことも一因だと思っているので、普段からRailsの動向をウォッチしてない人にも僕の活動だったり改善だったりが伝わるように、できるところからということでひとまずブログでアウトプットするのをがんばっていこうというのを今年の抱負としたいと思います。

165万払って全身脱毛をはじめた

全身脱毛を11月からはじめてみた。

前職のハイパーサポートエンジニアの同僚が尻の毛を脱毛したエントリを見て、たしかに尻の毛いらんな!と頭の片隅に残っていて、YouTubeでちょいちょいローランドのチャンネルを観ているのでそういえば新宿の新店舗ってどのへんなんやろって調べたらおもいのほか家の近所すぎてテンションあがったのでその日のうちに電話して翌日に全身脱毛の契約をしてしまった。

その後、カンジャンケジャンを食いながら今日全身脱毛契約してきてんって話をしたら「医療?美容?」って聞かれて、なるほどそういえばそういうの全然考えてなかったなと思って、もう金払ったあとなんでどうしようもないけど医療脱毛とか美容脱毛について調べてなるほどね〜となったのでその情報をシェアハピします。

医療脱毛と美容脱毛の違い

全身脱毛ってほぼレーザー脱毛なんじゃないかと思うけど、医療(クリニック)と美容(サロン)の違いは、医療のほうが威力の強いレーザーを照射して施術していいので効果が高く、より少ない回数で脱毛の効果が得られるけど威力が強いからめっちゃ痛いらしい。あと、医療のクリニックには医師免許を持った医師が常駐している必要があるらしく、また、医療機関なので麻酔を使ってもいいとのこと。

美容のサロンは逆にレーザーの威力が抑えめなので、効果が得られるのにより回数がかかるけどあんまり痛くないとのこと。あと医師が必要じゃないので開業の敷居が低い。さいきん手越くんやヒカルがオープンした脱毛サロンも、美容脱毛のサロンです。

ということで、どっちがいいのかは人それぞれかなというところで、痛みに耐えられるドMで爆速で効果を得たいひとは医療、痛いのは嫌だけど脱毛はしたいというよくばりさんは美容がいいのではないかと思います。

脱毛方法の種類と特徴

じつは8年ほど前にMEN'S TBCでヒゲ脱毛をやったことがあって、それはニードル脱毛だった。ニードル脱毛はおおまかにいうと、毛が生えてる毛穴に針らしきものを差し込んで電気や熱で毛根に直接ダメージを与えて脱毛する方式です。

最近の主流はレーザーを照射する方式の脱毛で、おおまかに分けると従来からある熱破壊式と最近流行ってる蓄熱式とがあって、それぞれレーザーによってダメージを与える部位が違う。

従来からある熱破壊式は、メラニン色素に反応するレーザーで毛根のメラニン色素に反応させ、その熱で毛根にダメージを与えて脱毛する。一方で最近流行ってる蓄熱式(SHR方式)は、毛根より浅いところにあるバルジ領域という発毛因子を持つ部位にダメージを与える方式で、熱破壊式よりも出力の弱いレーザーを連続で照射して脱毛するので、熱破壊式より痛みがマシというのが流行ってる理由だと思う。脱毛効果でいうと、熱破壊式のほうが痛いけど効果は高いとは思う。また、SHR方式の脱毛は流行っているけどその理論には懐疑的な意見も結構あって、詳細については君の目で確かめてみてほしい。

SHR式脱毛を詳しく | 院長コラム|銀座アイエスクリニック

SHR式脱毛 バルジ領域を65℃にするという事 | 院長コラム|銀座アイエスクリニック

美容脱毛は本当に痛くないのか

僕が行ってるROLAND Beauty Loungeは美容脱毛なので医療脱毛と比べてどうなのかはわからないけど、基本痛いって感じではないけど一部毛が濃い部位は痛い、あと痛いっていうより熱くて痛いって感じです。だいたい施術のときに「痛み大丈夫ですか?」って聞かれるけど、僕の場合、顔だと鼻下は「痛いっちゃ痛いけどまあ気合いっす」って答えてる感じだけど、Vゾーンのエクスカリバーの付け根あたりは「気合いっちゃ気合いだけどクッソ痛いっす」なぐらいには痛いです。

ちなみに、MEN'S TBCのニードル脱毛は一回の施術で針を100回ぐらい毛穴に刺すんだけど、その100回1時間ぐらい全部痛いです。

料金

脱毛って "全身脱毛 月3,300円〜" みたいな広告多すぎないですか?ほぼ全部の脱毛の広告や料金表が「で、総額いくらなん?」なのは正直どうなんかと思っている。

その点、MEN'S TBCのヒゲ脱毛は1本いくらとかだったんで明瞭会計だったんだけど、3,000本30万ぐらい払って3,000本施術し終わったとき、これ、ヒゲなくなるぐらいまで脱毛しよ思ったら100万ぐらいかかるなと思って追加の契約はしなかった。

もうひとつ正直どうなんかと思うところは、たとえば顧客が本当に求めている脱毛効果が10,000本100万ぐらいのときに、いきなり100万っていうたら契約取れなさそうやからとりあえず30万ぐらいから契約取って、あとから何回も追加で契約取るっていうのも、まあ世の中ってそういうもんなんかもしらんけどなんかな〜って感じっすわ。

まあそんなこんな思ってるところがあって、ROLAND Beauty Loungeの初回カウンセリングで予算の相談のときに「効果が感じられるならいくらでもいいですよ」って感じでいったら料金表には載ってないプランなんですけどって最上位プランと最上位一つ下のプランから説明してもらって、値段高すぎて引いたけど最終的に最上位プランで契約した。たぶん2周目の人生だったらどの部位は何回ぐらいでいいとかわかるけど、ヒゲのときのこともあるし、全部位フルベットしといたんであとは全力でいい感じにしてくれるってことでいいんですよねという感じ。

まだ施術は5回目なのだけど、サービスには満足してます。

おまけ

全身脱毛契約したあとのカンジャンケジャンです🦀

無職になってからやったこと(保険と給付金)

無職になってからのこと書こうと思ったら保険と給付金だけで力尽きました。

ハローワークで求職者登録

だいたいの会社員は雇用主によって雇用保険に加入しており、失業中にはいわゆる失業手当を受給できる。 せっかく保険料払っとるねんから一回ぐらい失業手当もらっとかなあかんなということでハローワーク(公共職業安定所)に通ってる。

ちなみに、ハローワークでいうところの "失業" とは、離職中のひとが "就職しようとする意思といつでも就職できる能力があるにもかかわらず職業に就けず、積極的に求職活動を行っている状態にある" ことをいうそうです。

ところで、失業手当がいくらもらえるのかざっと検索するとだいたい賃金の50~80%ぐらいって出てくるんで、え、そんなもらえたら無職のまま豪遊できてしまうで、と思ったけどそんなうまい話はなかった、基本手当日額には上限が存在していて、僕の場合は基本手当日額7,605円であった。

雇用保険と失業給付金 退職手続きマニュアル~役所・公的機関に行く前に~ |転職ならdoda(デューダ)

失業手当計算|失業保険はいくらもらえるの? | ファンジョブ

また、上記基本手当受給資格がある人が給付日数を1/3以上残して安定した職業に就いた場合に残り日数に応じてもらえる再就職手当というのもある。 僕の場合は所定給付日数180日で、再就職手当もらえるぐらいを目処に働くか〜ぐらいの気持ちでいたけど、気づいたらすでに受給日数120日余裕で超えてて再就職手当チャンスのがしてた…。

再就職手当ってどうやってもらうの?条件・計算方法や申請から受給までの流れを解説|20代・第二新卒・既卒向け転職エージェントのマイナビジョブ20's

これら失業等給付は言われてみればそうかって感じだけど非課税です。

国民年金の免除申請

収入の減少や失業等により国民年金保険料を納めることが経済的に困難な場合、保険料の一部または全額の免除が可能らしいです。

国民年金保険料の免除制度・納付猶予制度|日本年金機構

とくに経済的にどうということはなかったのだけど、ラーメン屋で麺の量どうしますか(大盛り無料)って言われたときに大盛りでっていう感覚で、とりあえず教えてもらったので免除にしてみた。が、先にハローワークで提出してしまっていた離職票が必要と言われてめちゃくちゃ面倒だった。

面倒だったわりに、免除はしないほうがよかった。まず、年金の保険料は所得控除の対象なのでむしろ払ったほうがよかった。あと、企業型確定拠出年金を個人型のiDeCo口座に移管するのに、免除中は移管の手続きができないということ。というか、新宿区役所iDeCoのこと聞こうとしたらiDeCoはここじゃないですって対応されたけど、年金保険料の免除を勧めるんやったらiDeCoへの移管ができなくなる副作用が存在するねんから関係なくはないし、そこの対応は改善してほしいと思いました。

ちなみに、年金保険料の納付書はあるので、ワンチャン支払いしたら免除解除されんかなと思ってやってみたけど、年金保険料過誤納額還付・充当通知書が届いて余計面倒なことになっただけなのでおすすめしません。

健康保険の任意継続

任意継続とは、退職後も前職の健康保険の制度に引き続き加入できる制度です。福利厚生などもあると思うけど、前職が加入してる組合の健康保険を任意継続するか国民健康保険に加入するかは単に保険料の額で決めてよいと言われたので、新宿区役所で保険料がいくらになりそうか聞いてみた。自力で計算してみてもあってるのかどうかわからんかったので。

保険料の計算方法について:新宿区

保険料の減免について:新宿区

保険料の額は? | [ITS]関東ITソフトウェア健康保険組合

結果、任意継続のほうが保険料は安かったけど、手違いで勧められるままに記入した紙が国民健康保険の加入手続きの用紙で、倍ぐらい保険料が高いほうに加入手続きが完了してしまって泣いた🥲

保険と給付金以外のこともそのうち書くかもしれません。

Rails 6.1で `created_at > ?` みたいなクエリをいい感じに生成する

Rails 6.1の目玉機能として以下のように書けるwhere拡張を入れてたんですが、いろいろあって6.1からはrevertされてしまいました🥲

posts = Post.order(:id)

posts.where("id >": 9).pluck(:id)  # => [10, 11]
posts.where("id >=": 9).pluck(:id) # => [9, 10, 11]
posts.where("id <": 3).pluck(:id)  # => [1, 2]
posts.where("id <=": 3).pluck(:id) # => [1, 2, 3]

github.com

github.com

なんですが、そんなことで引き下がる僕ではないので、6.1ではpredicate生成に干渉できる拡張ポイントを用意しており、以下のようなコードを適当に読み込まれるところにしたためておけば、いともたやすくwhere拡張を実現することができます。

ActiveSupport.on_load(:active_record) do
  ActiveRecord::PredicateBuilder.prepend Module.new {
    def [](attr_name, value, operator = nil)
      if !operator && attr_name.end_with?(">", ">=", "<", "<=")
        /\A(?<attr_name>.+?)\s*(?<operator>>|>=|<|<=)\z/ =~ attr_name
        operator = OPERATORS[operator]
      end

      super
    end

    OPERATORS = { ">" => :gt, ">=" => :gteq, "<" => :lt, "<=" => :lteq }.freeze
  }
end

ぜひ活用してくださいね😉

activerecord-importを利用して無効なデータを無理やりINSERTする

activerecord-importと:on_duplicate_key_ignoreオプションを組み合わせるとカラム定義の範囲外の値であっても無理やりINSERTすることができます。

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "activerecord", "6.1.0"
  gem "activerecord-import"
  gem "mysql2"
end

require "active_record"
require "logger"

ActiveRecord::Base.establish_connection(adapter: "mysql2", database: "test", username: "root")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :users, force: true do |t|
    t.string :name, index: { unique: true }
    t.decimal :money, precision: 10
  end
end

class User < ActiveRecord::Base
end

attributes = [
  { name: "foo", money: "10000000000" },
  { name: "foo", money: "20000000000" },
]

User.import(attributes, on_duplicate_key_ignore: true)

# User.insert_all(attributes)

puts
puts User.pluck(:money) # => 9999999999
puts
% ruby foo.rb
Fetching gem metadata from https://rubygems.org/..............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using concurrent-ruby 1.1.7
Using bundler 2.2.3
Using minitest 5.14.3
Using zeitwerk 2.4.2
Using mysql2 0.5.3
Using i18n 1.8.7
Using tzinfo 2.0.4
Using activesupport 6.1.0
Using activemodel 6.1.0
Using activerecord 6.1.0
Using activerecord-import 1.0.7
-- create_table(:users, {:force=>true})
D, [2021-01-06T14:04:10.296375 #81282] DEBUG -- :    (66.5ms)  DROP TABLE IF EXISTS `users`
D, [2021-01-06T14:04:10.377990 #81282] DEBUG -- :    (80.7ms)  CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` varchar(255), `money` decimal(10), UNIQUE INDEX `index_users_on_name` (`name`))
   -> 0.1967s
D, [2021-01-06T14:04:10.397652 #81282] DEBUG -- :   ActiveRecord::InternalMetadata Load (0.5ms)  SELECT `ar_internal_metadata`.* FROM `ar_internal_metadata` WHERE `ar_internal_metadata`.`key` = 'environment' LIMIT 1
D, [2021-01-06T14:04:10.417055 #81282] DEBUG -- :    (0.3ms)  SELECT @@max_allowed_packet
D, [2021-01-06T14:04:10.424171 #81282] DEBUG -- :   User Create Many (6.8ms)  INSERT IGNORE INTO `users` (`name`,`money`) VALUES ('foo',10000000000),('foo',20000000000)

D, [2021-01-06T14:04:10.428391 #81282] DEBUG -- :    (3.4ms)  SELECT `users`.`money` FROM `users`
9999999999

https://gist.github.com/kamipo/3db82c3bb7cbcbf007b4d4367a5c5227

これはどういう原理かというと、activerecord-importではINSERTしたいけどすでに(ユニークキーが)おなじレコードがあるときはスルーしたい(i.e. on_duplicate_key_ignore)という機能を実現するのにMySQLではINSERT IGNORE構文を使っていて、INSERT IGNOREではINSERT中のすべてのエラーを無視して無効な値は可能ならもっとも近い値に調整してINSERTするという振る舞いをするため、このような挙動を引き起こすことができます。

では、INSERTしたいけどすでに(ユニークキーが)おなじレコードがあるときはスルーしたい、けど無効な値はちゃんとエラーにしてほしいときはどうしたらいいかというと、Rails 6.0から使えるinsert_allというバルクインサート用のAPIを使うことができます。RailsチームではわいがMySQLチョットデキルので、この問題についてはレビューでフィードバックして対処されており安心してご利用になることができます。

github.com

See also

songmu.jp

それでは本年も引き続きよろしくおねがいいたします。