STUDIO PLUS TWO ブログ

横浜のウェブエンジニアチーム、STUDIO PLUS TWO(スタジオ・プラスツー)の技術ブログです。https://studioplustwo.jp/

Rubyでワンライナーを書く上で必要となる主なオプションの一覧

簡単な作業を実行したいときに、ワンライナーで手早く処理するケースはそれなりに多く、その際に手慣れた言語を使用できると非常に便利である。

ここでは、 Rubyワンライナーを書くために最低限必要となるオプション一覧をメモしておく。

以下に挙げるオプションさえ覚えておけば、最低限困ることはないはず...?

-e オプション

引数にファイル名を取らずに、引数に与えられた文字列をそのまま Ruby スクリプトとして解釈して実行する。

Rubyワンライナーを書く上で基本となるオプション。

$ ruby -e 'puts "ラーメン"'
ラーメン

-n オプション

標準入力として与えられた文字列の各行に対して Ruby スクリプトを実行する。各行の文字列は $_ 変数に格納される。

$ echo 'ラーメン\nそば\nうどん' | ruby -ne 'puts "カレー" + $_'
カレーラーメン
カレーそば
カレーうどん

-pオプション

スクリプト実行の最後に $_ の値をを標準出力に出力する。それ以外は -n と同じ。

$ echo 'ラーメン\nそば\nうどん' | ruby -pe '$_ = "カレー" + $_'
カレーラーメン
カレーそば
カレーうどん

-a オプション

-n-p と一緒に用いた際に、各行の文字列が split されて $F 配列に格納される。

なお、split メソッドのデフォルトの挙動は「先頭と末尾の空白を除いたうえで、空白文字列で分割する」。

$ echo 'ラーメン そば うどん\nカレー 牛丼 かつ丼' | ruby -ane 'puts $F[1]'
そば
牛丼

-l オプション

行末の自動処理(読み込み時の改行除去、出力時に行末に自動で改行を付与)を行う。

$_変数の末尾に改行が残っていることにより、意図しない結果が出力される場合の回避策として使用できる。

$ echo 'ラーメン\nカレー\nうどん' | ruby -ne 'puts $_ + "野郎"'
ラーメン
野郎
カレー
野郎
うどん
野郎

(↑ 改行が末尾に付いているため、意図しない結果になる)

$ echo 'ラーメン\nカレー\nうどん' | ruby -nle 'puts $_ + "野郎"'
ラーメン野郎
カレー野郎
うどん野郎

-iオプション

-p-nと併用することで、引数として指定されたファイルの内容を置換する。

-i.bak のように拡張子を指定すると、バックアップファイルとして指定拡張子が付いたファイルを作成する。

リポジトリ内の全てのソースコードの変数名を一括置換したり等、活用の場はそれなりに広い。

$ ruby -i.bak -pe '$_.upcase!' hoge.txt
(hoge.txt の中身の英文字を、全て大文字に置換し上書き。hoge.txt.bak というバックアップファイルを作成する。)

Ubuntu + PostgreSQL + PGroonga 環境における、PostgreSQL 14 へのアップグレード手順

www.postgresql.org

当スタジオにて運用中のシステムで稼働している PostgreSQL 13 系 を 14 系 にアップグレードしたときの手順を紹介。

当該システムでは PGroonga による全文検索機能を実現しているのだが、PGroonga 2.3.2 により PostgreSQL 14 系がサポートされるようになったため、満を持してアップグレードを実施することに。

参考: https://pgroonga.github.io/ja/news/#version-2-3-2

はじめに注意点

PGroonga をアップグレード → その後に PostgreSQL をアップグレードという手順も試したものの、14系対応バージョンのPGroongaでも、13系のPostgreSQLライブラリでビルドした場合、13系用バージョンとなってしまい14への移行時に version mismatch エラーが発生する模様。

PostgreSQL 14系のライブラリのみをまず入れる」→「14系用のPGroongaをビルド&インストール」→「PostgreSQL 14本体をインストール&13からのアップグレード」という、少々トリッキーな手順で実施すればうまくいく可能性もあるが、今回は PostgreSQL を先にアップグレードする手順を採用した。(後者の場合、インデックスの再構築は必要となる一方、手順に紛れがないので、インデックス再作成により停止時間が長くなるのを許容できるのであればおススメ)

アップグレード前の構成

PostgreSQL 14 のインストール

まずは作業前に、PostgreSQLのデータバックアップを実施。

Ubuntu では、pg_upgradecluster をはじめとした専用のコマンドが存在するため、今回はこれらを使用していく。

lets.postgresql.jp

PostgreSQL の公式 apt リポジトリを追加していない場合は、以下の手順に従って追加作業を実施。

www.postgresql.org

PostgreSQL 14のサーバ/クライアント/ビルド用ライブラリをインストール。

$ sudo apt install postgresql-14 postgresql-client-14 postgresql-server-dev-14

インストール後は、PostgreSQLクラスタに自動追加されるため、 pg_dropcluster コマンドにより PostgresSQL 14を手動で削除。

$ pg_lsclusters
(...14系がインストールされていることを確認)
$ sudo pg_dropcluster 14 main --stop

さらに PostgreSQL サービスを停止した後、pg_upgradecluster コマンドにより13からのアップグレードを実施。

アップグレードにはデータサイズに応じた時間を要する。

$ sudo systemctl stop postgresql.service
$ sudo pg_upgradecluster 13 main

pg_upgradecluster でこんなエラーが出て、全文検索インデックスのみ移行がスキップされる形となるが、アップグレード自体は成功する。

ERROR:  incompatible library "/usr/lib/postgresql/14/lib/pgroonga.so": version mismatch
DETAIL:  Server is version 14, library is version 13.
ERROR:  extension "pgroonga" does not exist
ERROR:  access method "pgroonga" does not exist

アップグレード後は PostgreSQL を起動

$ sudo systemctl start postgresql.service

Groonga のアップグレード

Ubuntu 20.04 標準で利用できる Groonga だとバージョンが足りないため、まずはGroongaのアップグレードを実施。

参考 : https://groonga.org/ja/docs/install/debian.html

$ wget https://packages.groonga.org/debian/groonga-apt-source-latest-buster.deb
$ sudo apt install -y -V ./groonga-apt-source-latest-buster.deb
$ sudo apt update
$ sudo apt install libgroonga-dev

PGroonga のビルド

参考: https://pgroonga.github.io/ja/install/ubuntu.html

前述通り、2.3.2以降であれば PostgreSQL 14 に対応している。本記事を執筆時点の PGroonga 最新版は 2.3.4 なのでこちらを採用。

$ wget https://packages.groonga.org/source/pgroonga/pgroonga-2.3.4.tar.gz
$ tar xvfz pgroonga-2.3.4.tar.gz
$ cd pgroonga-2.3.4/
$ make HAVE_MSGPACK=1

PGroongaのアップグレード

システム側のプロセスを全停止し、PGroongaをインストール。

$ sudo make install

全文検索インデックスの再作成

管理ユーザで psql を起動し、psql上で以下のSQL文を実行し、pgroonga拡張を作成。

CREATE EXTENSION pgroonga;

さらに以下のSQLを実行し、全文検索インデックスを再作成。(インデックス名、テーブル名はダミー)データサイズに応じた処理時間がかかる。

CREATE INDEX index_hogehoge_on_text ON hogehoge USING pgroonga (text);

全文検索機能が正しく機能することを確認して、作業終了。

Amazon OpenSearch Service 設定変更時の Blue/Green デプロイについて

TL;DR

  • Amazon OpenSearch Service では、ドメインの合計ノード数が1の場合でも、ほぼ無停止で設定の変更ができる。
  • 設定変更が完了するまでは、旧設定のままでドメインが稼働する。
  • ノード数が1の場合停止時間が発生する Elastic Cloud とは挙動が異なるため、OpenSearch Serviceに慣れていないと「ノードが止まってない=設定は即時反映されたのか?」と誤解する可能性がある

どういうことか

docs.aws.amazon.com

OpenSearch Service では、ドメインの更新時に Blue/Green デプロイプロセスが使用されます。Blue/Green は通常、2 つの本稼働環境 (ライブとアイドル) に使用して、ソフトウェアの変更時に両者間で切り替える方法を指します。OpenSearch Service の場合は、ドメインの更新用に新しい環境を作成し、これらの更新の完了後に新しい環境にユーザーをルーティングする方法を指します。この方法では、新しい環境へのデプロイに失敗しても、ダウンタイムを最小限に抑えることができ、元の環境を維持することができます。

Blue/Green デプロイ

管理コンソール上で OpenSearch ドメインのセキュリティ設定を変更しようとすると、以下のようなメッセージが表示される。

f:id:studioplustwo:20211110155002p:plain

暗号化設定を更新するために、Amazon OpenSearch Service はパフォーマンスに影響を与える可能性のある Blue/Green プロセスを使用します。

OpenSearch Service でドメインの設定変更を行う場合、

  • 新しい設定を適用した新ノードを起動
  • 新ノード起動後、ネットワークを旧ノードから新ノードへ切り替え

という手順を踏むことで、稼働状態を維持したまま設定が変更される。この挙動は「Blue/Greenデプロイ」と呼ばれる。

f:id:studioplustwo:20211111155854p:plain

設定適用中は、一時的にノードの数が自動的に増加する。(多くの設定変更の場合、追加料金はかからないようだ)

これにより、OpenSearch Serivceでは設定の変更反映中でも、普通にドメインにアクセスすることができる。

変更反映中かどうかの表示がわかりづらい

ただし、注意しなければならない点があり、設定変更中かどうかの表示が微妙にわかりづらいのである。

f:id:studioplustwo:20211111153521p:plain

ドメインの一覧表示。設定変更中の場合は、ステータス欄に「処理中」と表示される。

f:id:studioplustwo:20211111153727p:plain

一方、設定変更が完了した通常時は「アクティブ」と表示される。

変更反映中は、古い設定内容が適用される

Blue/Green デプロイによる変更反映中は、前述の通り通常のアクセスが可能である。

ただし、変更反映中は旧設定が適用され、変更が完了したタイミングで即時新設定が適用される、というような動作になる。

…上記の挙動は当然といえば当然なのだが、「ドメインが停止し、再起動完了後は新設定になっている」でも「変更ボタンを押したタイミングで即時新設定となる」でもない点に注意。

設定が適用されたタイミングがわかりづらいかも?

OpenSearch Service では、たとえドメインに1ノードしか存在しない場合でも、設定変更によりドメイン全体が停止することはない。

この挙動は、2ノード以上の場合 ローリングで設定が適用される(=1ノードの場合はドメイン停止時間が発生する) Elastic Cloud とは異なるものである。

そのため、Elastic Cloudに慣れている人の場合、

  • 「1ノードなのに設定変更でドメインが停止しない」
  • →「設定は即時反映されている?」

...という錯覚に陥る可能性がある。(現に私はそうでした...)

また上述の通り、設定変更中から設定変更完了状態に移行したタイミングが若干分かりづらい。

そのため、特にセキュリティ設定を変更している場合は、あるタイミングで接続できたものが突然接続できなくなる(またはその逆)という不可解現象に悩まされる可能性もあり、1ノードでの検証・テスト運用などでは注意が必要。

以下所感

Blue/Green デプロイの挙動については、1ノードでも停止時間が発生しないため非常に便利なものだという認識だが、如何せんステータスの表示が少々分かりづらいという点に問題があるように思う。

Elastic Cloud だと、設定を変更した場合は、変更中である旨がデカデカと表示されて誤解がないようになっていた記憶があるので、Amazon もその辺の表示をもっと親切丁寧に改善してほしいなあ...と思った昨今。

UnicodeをRubyで扱うときに使用する操作いろいろ

ある文字のUnicodeコードポイントを知りたい、または逆にコードポイントから文字を調べたい....と思うことがよくあるので、irbで打ち込むいろいろな操作のメモ。

(以下の例では、Rubyの文字列リテラルエンコーディングUTF-8の前提。)

コードポイント → UTF-8文字

Integer#chrメソッドを使用。

> 0x3042.chr(Encoding::UTF_8)
# => "あ"

コードポイント配列 → UTF-8文字列

配列要素単位で Integer#chr、またはまとめて Array#pack

>  [0x3042, 0x3044, 0x3046, 0x3048, 0x304A].map {|n| n.chr(Encoding::UTF_8)}.join
#=> "あいうえお"

または

>  [0x3042, 0x3044, 0x3046, 0x3048, 0x304A].pack('U*')
#=> "あいうえお"

UTF-8文字 → コードポイント

String#ord メソッドを使用。(返り値はArrayとなるが、String#codepointsでもよい)

> ''.ord.to_s(16)
#=> "3042"

UTF-8文字列 → コードポイント配列

  • 1文字ずつ区切ってString#ord
  • String#codepoints で一気に配列化(または String#each_codepoint
  • String#unpack
> 'あいうえお'.split('').map{|c| c.ord.to_s(16) }
#=> ["3042", "3044", "3046", "3048", "304a"]

または

> 'あいうえお'.codepoints.map{|n| n.to_s(16) }
#=> ["3042", "3044", "3046", "3048", "304a"]

または

> 'あいうえお'.unpack('U*').map{|n| n.to_s(16) }
#=> ["3042", "3044", "3046", "3048", "304a"]

Unicode正規化

String#unicode_normalizeメソッドを使用。

> '神㌍'.unicode_normalize(:nfkc)
#=> "神カロリー"

Elasticsearch の kuromoji-analysis plugin に含まれる filter について淡々と説明

Char filter

kuromoji_iteration_mark

踊り字(々、ゝ、ゞなど)を正規化する。

normalize_kanjitrue の場合、漢字の踊り字(々)を正規化、normalize_kanatrueの場合、かなの踊り字(ゝ、ゞ、ヽ、ヾ)を正規化する。

デフォルトは、normalize_kanjinormalize_kana ともに true

コレが必要になったケースはあまり経験したことがない...。

つねゞゝ -> つねづね
馬鹿々々しい -> 馬鹿馬鹿しい

Token filter

kuromoji_baseform

動詞/形容詞の各活用形を終止形に変換する。

飲ま -> 飲む

kuromoji_part_of_speech

話し言葉で使われる特定の品詞を除去する。

具体的にどの品詞が対象になるのかは、Lucene の stoptags.txt ファイルを参照。

基本的に入れたほうがいい filter であるが、形態素解析器に対象品詞と判断されるような固有名詞(ハンドルネームとか)が登場した場合、逆に検索に全く引っ掛からなくなることがあるため、その場合は都度ユーザ辞書等で対処していく必要がある。

github.com

寿司がおいしいね -> 寿司, おいしい

kuromoji_readingform

漢字→読みがな(カタカナorローマ字)に変換する。

読みがなで検索させたい、等の用途があれば使用できる。

東京 -> トウキョウ

kuromoji_stemmer

単語末尾の長音記号(ー)を除去する。

末尾長音記号の除去前後が同一とみなされて困るケースは少なく、基本何も考えずに入れておいても良いと思われる。

プログラマー -> プログラマ

cjk_width

全角英数記号を半角英数記号、半角カタカナを全角カタカナに変換する。

上記の変換がUnicode正規化の互換分解(Compatibility Decomposition)の一部であり、機能面で analysis-icu に含まれる icu_normalizer のサブセットになっているのと、char filter である icu_normalizer のほうが、 tokenize 前に処理される分使い勝手が良く、こちらを敢えて使う必要性はあまり感じられない。

@12345->@12345
ラーメン -> ラーメン

ja_stop

ストップワード(あまりに一般的であるため、検索文字列として意味が薄くなってしまう単語)を除去する。

どの単語が対象になるかは、Lucene の stopwords.txt を参照。

使用することで使いやすい検索機能となる一方、kuromoji_part_of_speech 同様、ストップワードを含む固有名詞を認識させるために、ユーザ辞書と併用する必要が出てくるときもある。

github.com

このラーメンはおいしい -> ラーメン, おいしい

kuromoji_number

漢数字→半角数字に変換する。

漢数字と英数字を同一視したいケース、たとえば住所の地番を正規化する用途であれば使えるかもしれない。

二〇二一 → 2021