ssh接続もシリアル接続もできなくなったGoogle Compute Engineで動くサーバーを復旧させる
何が起きたか
- 社内システムの動いているサーバーをメンテしようとした
- 突然GCEのいつものssh接続ができなくなった
- 冷や汗をかきながらシリアル接続を有効化するが、ログインできず
- 自分だけが締め出されたかと思って別ユーザーでも試すがログインできず
状況確認
- 基本的なステータスチェック(権限、ネットワークとか)はOK
- ssh接続しようとするとPermissin Deniedする
- 起動スクリプトを介して公開鍵を登録したりユーザーパスワードをリセットしてもログインできない
- 基本的なLinux力が足りていない
原因
- slackのWebhookが使えなくなった(Let’s Encryptの証明書が更新された)関係でいろいろいじったので、心当たりがありすぎる
- 証明書を再インストールしたあたり、バックアップとらずにそぉい!とやったのがまずかった気がする
- サーバーのdaemon自体は普通に動き続けていたため、普通に使われている(今日まで気づかなかったし)
- ここ最近のデータを失うのはツライ
- データはどうにか救わねばならない
対応
- このサーバーで動いていたのは社内用のRedmineで、なおかつbitnamiパッケージで導入したので詳しい構成はわからない
/opt
配下にいろいろインストールされており、なおかつ正常に動いていた2週間前のバックアップは存在する- なので、「正常に動いていた時代のバックアップ」と「最新のデータ」を合体させて起動させれば何とかなるんじゃないか?という対処を考える
やった手順
- 壊れたサーバーのSnapshotをとってディスクAを作成する
- 2週間前のバックアップからディスクBを作成する
- Compute Engineのインスタンス(復旧くん)を新規に作成して、ディスクA/ディスクBをマウント
- 復旧くんの中でディスクA/optをディスクB/optにrsync
- 復旧君からディスクBを外す
- 壊れたサーバーの起動ディスクをディスクBに切り替える
- 起動
反省
FlutterのIsolate.spawnはやはり重いという話
Windows Desktopビルドで重い処理を裏で行ったらやっぱり重い
- 画像のデコード、変換、解析といった重い処理をバックグラウンドで行おうとした
- Flutterのcompute()関数を使用してawaitすると、1秒以上かかる
- 実際の解析処理は0.2秒程度
- 流石に重いので計測すると、何も行わない空の関数でも1秒弱実行にかかった
- UIがブロッキングすることは無くなったが、そもそも時間がかかりすぎる
WindowsのHello Worldで計測
- flutter createした直後で同じように空の関数をコールして計測
- 500ms〜600ms程度かかる
- 遅い
解決方法
- 起動したIsolateをキャッシュするようにした
- ほぼ解析時間だけになった
- プログラムは複雑になった
- Isolateの起動は遅い。
XCode 14にアップデートしてflutter buildが通らなくなった際の対処
原因
- XCode 14からCocoaPodsの生成物(Pod)にも署名を要求するようだ
- n=1なので、他の不要な場合もあるかもしれない
- Issueがある
対応
- cocoapods-pod-signをインストール
- Flutterは
/usr/bin/gem
のPATHを優先的に検索するようなので、複数バージョンのgemをインストールしてる場合は標準gemでinstallするほうが面倒がなくて良い
gem install cocoapods-pod-sign
cocoapods-pod-sign
プラグインをPodfileに追加
platform :ios, '15.0' plugin 'cocoapods-pod-sign'
Flutter + Redux Architectureでパフォーマンスチューニング
パフォーマンスチューニングが必要になった理由
- プロジェクトでRedux Architectureを採用したが、求める機能に合致するライブラリがないためチーム内で自作した pub.dev
- これにより求める機能が実装されたが、プロジェクトでは特定条件下でState更新が1000回 / 秒を超える場合がある
- UI = f(State)を愚直に実装していたため、Widget.build()が1000回 / 秒走ることとなり、パフォーマンスに明らかな悪影響が出た
解決方法
- Stateを流すためのStreamと、WidgetをビルドするためのStreamを分けた
- StateをハンドリングするためのStreamは取りこぼしがあってはならない(イベントハンドリングなど)ため、実装をそのままとした
- それとは別に、WidgetをビルドするためのStreamを追加した
- こちらは最大で60fpsでデータが流れる仕様となっているため、仮に1000回 / 秒State更新が走ってもWidgetに反映されるのは60回 / 秒が最大である
/// レンダリング用に流通量を制限したStreamを生成する. BehaviorSubject<TState> _initializeRenderStream(Duration renderingInterval) { final result = BehaviorSubject<TState>.seeded(state); _subscription.add( Stream.periodic( renderingInterval, (computationCount) => state, ).distinct().listen(result.add), ); return result; }
- これによりWidget.build()のスパイクによる異常rebuildを回避してパフォーマンスが向上した
flutter build ipaのad-hocビルドが異様に遅い問題の対応
発生した問題
- AppStore用ビルドは20分前後でビルドが完了する
- 検証用のAd-Hocビルドが60分以上時間がかかる
- XCode 13.2.1, Flutter 2.10.4
試したこと
- Swift/Clangの最適化レベルを変更 -> 効果なし
- dSYM出力無効化 -> 効果なし
突き止めた問題
--export-options-plist
でAd-Hoc用ビルドにするとビルド時間が3倍に伸びる- 試しにビルド20分のAppStore用ビルドをAdHoc用のplistに切り替えるとビルド時間が同じくらいに遅くなった
- ログを観察しても、ipa出力時に異様に時間がかかっているので間違いなさそう
plist仕様を確認
- plist仕様を確認し、AdHoc用途で不要そうでdefault trueの項目をチェック
- これらを明示的にfalseにしたところ、ビルド時間が20分まで早くなった
<key>uploadBitcode</key> <false/> <key>compileBitcode</key> <false/>
compileBitcode
が一番怪しいけど、そこまで切り分けはツラいのでこれでヨシとする- なんでこのオプションで異様に時間がかかるのか(appstoreビルドではtrueでも影響ないのか)はよくわからない
dartのFreezedとPImplイディオムでメンバ保護を行う
前提
Flutterの開発でReduxアーキテクチャを採用している。
Stateの管理に freezed を使うと、簡単な代わりに全てのメンバがpublicになってしまう。
一部のメンバはprivateにしたり、計算済みの値をキャッシュしたい。
改善
概念。
@freezed class ExampleStateImpl with _$ExampleStateImpl { factory ExampleStateImpl({ /// このデータはpublicにしたくない. /// dataListの合計計算は必要になる時まで計算を遅延したい required List<int> dataList, }) = _ExampleStateImpl; const ExampleStateImpl._(); }
class ExampleState { // Stateの実装を隠す final ExampleStateImpl _impl; factory ExampleStateImpl() { return ExampleStateImpl._(ExampleStateImpl(dataList: [])); } ExampleStateImpl._(this._impl); final int? _dataListSumCache; int get dataListSum { // 合計値を計算して返す。 // 計算済みのキャッシュがあればそっちを返す } ExampleState addData(int newValue) { return ExampleState(_impl.copyWith( // Freezedの機能を使ってデータコピーを返す )); } }
実際はもっと複雑なパターンとなるが、Freezedのメンバーを直接見せたくないけどcopyWithの恩恵は欲しい場合に使う。
dartでKotlin.internal funっぽいことをする
dartのinternal fun
(dart 2.15には)ない(たぶん)。
特定packageだけでアクセスしたいメソッドとか作れない。
作ったらみんなアクセスされてしまう。
解決方法
// src/example.dart class Example { String _value = ''; String get value => _value; } extension ExampleSetValue on Example { void setValue(String s) { _value = s; } }
library example; // exportするときに、Extensionを隠してやる export 'src/example.dart' hide ExampleSetValue;
これで行儀良く使っている限りは外部のpackageはExample.setValue()をコールできない。
package内は 'src/example.dart' を直接importする。
無理矢理importすれば使えなくもないので、lintと併用で。