brewでmacのpostgresを14にアップグレードした際にpgを使ってるrailsが立ち上がらなくなった。(M1 Macです)

/Users/komagata/go/src/github.com/fjordllc/bootcamp/vendor/bundle/ruby/3.1.0/gems/bootsnap-1.13.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require': dlopen(/Users/komagata/go/src/github.com/fjordllc/bootcamp/vendor/bundle/ruby/3.1.0/gems/pg-1.4.4/lib/pg_ext.bundle, 0x0009): Library not loaded: /opt/homebrew/opt/postgresql/lib/libpq.5.dylib (LoadError)
  Referenced from: /Users/komagata/go/src/github.com/fjordllc/bootcamp/vendor/bundle/ruby/3.1.0/gems/pg-1.4.4/lib/pg_ext.bundle
  Reason: tried: '/opt/homebrew/opt/postgresql/lib/libpq.5.dylib' (no such file), '/usr/local/lib/libpq.5.dylib' (no such file), '/usr/lib/libpq.5.dylib' (no such file), '/opt/homebrew/Cellar/postgresql@14/14.5_5/lib/libpq.5.dylib' (no such file), '/usr/local/lib/libpq.5.dylib' (no such file), '/usr/lib/libpq.5.dylib' (no such file) - /Users/komagata/go/src/github.com/fjordllc/bootcamp/vendor/bundle/ruby/3.1.0/gems/pg-1.4.4/lib/pg_ext.bundle
/
  opt/
    homebrew/
      opt/
        postgresql/
          lib/
            postgresql@14/
              libpq.5.dylib <- 有る
            libpq.5.dylib <- 無い

/opt/homebrew/opt/postgresql/libの中にはlibpq.5.dylibが無くてpostgresql@14ってディレクトリの中にある。なんかシンボリックリンク作成やコピーのミスのような雰囲気がするけど、とにかくライブラリの位置が変わってるっぽい。

とりあえずシンボリックリンクを貼ったら動いた。HomebrewのFormulaのreposを覗いてみたけどちょっとよくわからなかった。

$ ln -s /opt/homebrew/opt/postgresql/lib/postgresql@14/libpq.5.dylib /opt/homebrew/opt/postgresql/lib/libpq.5.dylib

productionのデータでのみ発生するバグを調査するときなど。

$ dropdb foo_development
$ heroku pg:pull postgresql-foobarbuz-1234 foo_development -a foo

「知り合いの凄腕エンジニアに聞いたんだけど、/users/32ってURLにユーザー数が丸見えになってるって、このセキュリティーは流石にまずいよ、駒形さん」

みたいに言われることが稀によくあるので、idをuuidにしました。

postgresの設定

rails5からpostgresのuuidを生成する関数を簡単にidとして使えるようになってるそうです。 herokuのpostgresは対応してるのでいいかもです。

Macのbrewで入れたpostgresはデフォルト無効なので有効にしてあげる必要がありますが、superuserじゃないと駄目です。

postgres=# SELECT uuid_generate_v4();
ERROR:  function uuid_generate_v4() does not exist

↑関数がない。

デフォルト状態ならばsuperuserはpostgresかつパスワード無しなので、下記をやっといてくださいと各位に伝えます。

$ psql -d postgres -c 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'
postgres=# SELECT uuid_generate_v4();
           uuid_generate_v4           
--------------------------------------
 06162241-ad98-4795-8bab-3801194f76d7
(1 row)

関数が有効になりました。

railsの設定

generatorがデフォルトでuuidを使うようにします。

# config/application.rb:
module Foo
  class Application < Rails::Application
    config.generators do |g|
      g.orm :active_record, primary_key_type: :uuid
    end
  end
end

こちらを参考にidのdefaultを設定する。

2 UUID Primary Keys

class PostProperties < ActiveRecord::Migration[5.1]
  def change
    create_table :posts, id: :uuid, default: "gen_random_uuid()" do |t|
      t.string :title
      t.text :description
      t.belongs_to :user, foreign_key: true, type: :uuid

      t.timestamps
    end
  end
end

foreign_keyを設定する時に、type: uuidを忘れないこと。

# SELECT id FROM users;
                  id                  
--------------------------------------
 75a61708-a959-5889-83cd-4d86df9cd771
 7c6b7130-3896-53a1-938d-2ab4364011d2
 f94db8c4-6786-5027-8d90-aa187594addb
 bafbab57-d268-548a-a111-11c13bbcbcc2
 2fc060cd-98d2-58d2-9b33-50f36df1fd8f
 8338b729-ff29-55d2-b5ba-6d68dbfc7b03
 917358a7-8995-5d88-b635-925e21841f68
 529aa2fb-93b3-5739-a8d4-64dd87ed08a9
 6ea9b086-d16b-5b02-81b6-97edb74f3dfb
 71f372c5-b0cc-5904-a583-921766bf4508
(10 rows)

ウェーイ。

おもてなし

何かと外見も重要な、出資を受けたスタートアップのサービスのアプリを開発する時にはさらっとやっておくとよいかもしれません。

ActiveRecordのデフォルトではORDER BY区は省略されてるので、「ポスグレでデフォルトの並びってどうなってるんだろう?」と思って調べました。

SELECT - PostgreSQL 9.3.2文書

ORDER BYが指定されない場合は、システムが計算過程で見つけた順番で行が返されます

(略)

ORDER BY句を使うと、結果行を指定した式(複数可)に従ってソートすることができます。 最も左側の式を使って比較した結果、2つの行が等しいと判断された場合は、1つ右側の式を使って比較します。その結果も等しければ、さらに次の式に進みます。 指定した全ての式で等しいと判断された場合は、実装に依存した順番で返されます。

なんとなくINSERT順で出るのでは?と思ってましたが、実装依存なようなので、並びに意味がある箇所では必ず明示的に指定したほうが良さそうです。

SQL92や99、MySQLやSQLiteではどうなってるんだろう?

default_scopeでid ascとかにしとくべきか?でもARのdefault_scopeってバグを踏みやすい勝手な印象が付いてしまっていて過信するのもためらわれる…。

% heroku plugins:install git://github.com/ddollar/heroku-sql-console.git
% heroku sql
SQL> select * from unk;

It's not psql. It's only send sql and return results.

$ curl -o latest.dump `heroku pgbackups:url`

所謂上記で取れるdumpファイルはPostgreSQLマニュアルで言うところの”非プレインテキスト形式のアーカイブ”なのでpg_restoreじゃないとリストア出来ない。不便に思うけど、pgbackupsというサービスを作る事を考えたら柔軟なデータ形式よりも、同じDBだったらroleとか全て取りこぼし無く復旧してくれそうな形式になっているのは納得出来る話だ。(ちょこっとcommentsテーブルだけ取りたいなんて今回の自分の用途にとってはうざいが・・・)

$ pg_restore -d foo foo.dump

roleは違うけどデータは取れた。

$ heroku pgbackups:capture --expire

個数制限があるのでこれで古いのを消しつつ新しいのをバックアップしてくれる。楽だなあ。

$ brew install postgresql
$ initdb /Users/komagata/homebrew/var/postgres
$ cp /Users/komagata/homebrew/Cellar/postgresql/9.0.2/org.postgresql.postgres.plist ~/Library/LaunchAgents
$ launchctl load -w ~/Library/LaunchAgents/org.postgresql.postgres.plist

起動。

$ pg_ctl -D /Users/komagata/homebrew/var/postgres -l /Users/komagata/homebrew/var/postgres/server.log start

終了。

$ pg_ctl -D /Users/komagata/homebrew/var/postgres stop -s -m fast

滅多に使わないので立ち上がらないようにunloadしておく。

$ launchctl unload -w ~/Library/LaunchAgents/org.postgresql.postgres.plist
% psql -l
                                  List of databases
   Name    |  Owner   | Encoding |  Collation  |    Ctype    |   Access privileges  
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | komagata | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |
 template0 | komagata | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/komagata          +
           |          |          |             |             | komagata=CTc/komagata
 template1 | komagata | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/komagata          +
           |          |          |             |             | komagata=CTc/komagata
(3 rows)

昔はなかったAccess privilegesなんて表示が追加されてる。CTcってのはどういう意味だろう。Capture The Flag?