初gemを作成した
Logrageを利用していてActiveJobのログも出力したかったのでgemを作った。
gem作成から公開までをやったのは初めてだったのですごくドキドキだったけど、やったみて良かった。
なんで作ったの?
Logrageは下記のようにaction_controllerに対してLogrageで用意したLogSubscriberを登録しているので、コントローラーを通るものしかログが出ない。
https://github.com/roidrage/lograge/blob/v0.10.0/lib/lograge.rb#L142
非同期処理にActiveJobを利用していて、そのログもlogrageで同様に出力したいなと思ったのがきっかけ。
どういう仕組み?
まあ、実装はほとんどLogrageの実装と同じ感じなんだけど。
RailsにはActiveSupportのInstrumentationというものがあり、Railsで色々イベントが用意されているので開発者がフックできる。
Active Support の Instrumentation 機能 | Rails ガイド
Logrageもこの中のprocess_action.action_controllerを利用してログの出力を行なっている。
なので、コントローラーを通らないとこのイベントが発生しないのでActiveJobのログはlogrageでは出力されない。
ActiveJobにもxxx.active_jobというイベントが設定されているので、これを利用してLogrageでログ出力を行なっている。
全て対応しようかとも思ったけど、今回のユースケースはjobの処理が終わった時の情報(エラーが起きたらエラーの情報も)が欲しかっただけなので、今回はperform.active_jobのみ対応することにした。
これからもチャンスがあれば積極的にGemを作っていきたい。
lambda(python)で定期的にAthenaのMSCK REPAIR TABLEを行う
Athenaを使うようにして、パーティションの更新を都度行うのが面倒だったので自動的に行われるようにした。
下記のようなlambda関数を作成して、Cloudwatchでcron式で定期実行ルールを設定した。
import boto3 client = boto3.client('athena') def lambda_handler(event, context): sql = 'MSCK REPAIR TABLE テーブル名;' print('sql=' + sql) client.start_query_execution( QueryString=sql, QueryExecutionContext={ 'Database': 'データベース名' }, ResultConfiguration={ 'OutputLocation': 's3://実行結果の保存先バケット' } )
CloudFormationでElasticache for redisの設定をする
CloudFormationで非クラスターモード(マルチAZ)の構成を試してみた。
プライマリーノード:1、リードレプリカ:2の3ノード構成でプライベートなVPCに配置する想定。
最終的には下記の公式ドキュメントのサンプルオペレーションをCloudFormation対応した感じ。
次のオペレーションでは、3 つのノード、1 つのプライマリ、2 つのレプリカを持つ Redis (クラスターモードが無効) レプリケーショングループ new-group を作成します。
一部抜粋だけどこんな感じになった。
ECacheSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupName: !Ref ECacheSecurityGroupName GroupDescription: Security Group for Elasticache Redis SecurityGroupIngress: - IpProtocol: tcp CidrIp: '0.0.0.0/0' FromPort: 6379 ToPort: 6379 - IpProtocol: tcp CidrIpv6: '::/0' FromPort: 6379 ToPort: 6379 Tags: - Key: Name Value: !Ref ECacheSecurityGroupName VpcId: !Ref VpcId ECacheSubnetGroup: Type: "AWS::ElastiCache::SubnetGroup" Properties: CacheSubnetGroupName: !Ref ECacheSubnetGroupName Description: Redis Subnet Group SubnetIds: - !Ref SubnetAId - !Ref SubnetCId ECacheRedis: Type: "AWS::ElastiCache::ReplicationGroup" Properties: AutomaticFailoverEnabled: true AutoMinorVersionUpgrade: true CacheNodeType: !Ref ECacheNodeType CacheParameterGroupName: default.redis3.2 CacheSubnetGroupName: !Ref ECacheSubnetGroup Engine: redis EngineVersion: 3.2.10 NumCacheClusters: 3 ReplicationGroupDescription: 'Elasticache Redis' ReplicationGroupId: !Ref ECacheClusterName SecurityGroupIds: - !Ref ECacheSecurityGroup SnapshotRetentionLimit: 5 SnapshotWindow: 'sun:13:30-sun:14:30'
VPC内に配置したい場合
所属するサブネットグループをAWS::ElastiCache::SubnetGroupで作成して、CacheSubnetGroupNameで指定してあげる。
マルチAZにしたい場合
ドキュメントに記載がある。
注記 2.8.6 より前のバージョンの Redis または T1 キャッシュノードタイプでは、自動フェイルオーバーを有効にすることはできません。 自動フェイルオーバーは、クラスターモードが有効で Redis バージョン 3.2.4 以降を実行している場合のみ、T2 ノードタイプでサポートされます。
つまりT2系の場合はクラスターモードが有効でないとマルチAZにできない。
m3系以上だとクラスターモードが無効でもマルチAZにできる。
キャッシュノード数を2以上にしたい場合
初めはAWS::ElastiCache::CacheClusterでRedisを構築しようとしていたが、ノード数2以上で作成しようとするの下記のようなエラーがでる。
NumCacheNodes should be 1 if engine is redis
キャッシュノード数を2以上にしたい場合は下記のAWS::ElastiCache::ReplicationGroupを使う必要があるらしい。
AWS::ElastiCache::ReplicationGroup - AWS CloudFormation
下記の記事も詳しくてよかった。
クラスターモード有効時
検証用にクラスターモードの場合も構築してみた。
ECacheRedis: Type: "AWS::ElastiCache::ReplicationGroup" Properties: AutomaticFailoverEnabled: true AutoMinorVersionUpgrade: true CacheNodeType: !Ref ECacheNodeType CacheParameterGroupName: default.redis3.2.cluster.on CacheSubnetGroupName: !Ref ECacheSubnetGroup Engine: redis EngineVersion: 3.2.10 NumNodeGroups: 2 NodeGroupConfiguration: - PrimaryAvailabilityZone: ap-northeast-1a ReplicaAvailabilityZones: - ap-northeast-1a - ap-northeast-1c - PrimaryAvailabilityZone: ap-northeast-1c ReplicaAvailabilityZones: - ap-northeast-1a - ap-northeast-1c ReplicasPerNodeGroup: 2 ReplicationGroupDescription: 'Elasticache Redis' ReplicationGroupId: !Ref ECacheClusterName SecurityGroupIds: - !Ref ECacheSecurityGroup SnapshotRetentionLimit: 3 SnapshotWindow: 'sun:13:30-sun:14:30'
CloudFormationのAWS::EC2::InstanceでNetworkInterfacesとSecurityGroupIdsは一緒に使えない
CloudFormationでEC2のインスタンスを作成しようとした時に下記のエラーが出てハマった。
Network interfaces and an instance-level security groups may not be specified on the same request
解決方法としては下記の通りNetworkInterfacesとSecurityGroupIdsは一緒に使えないのでNetworkInterfaces中のGroupSetで設定しましょう。
ActiveJob with sidekiqでリトライをしないようにする
非同期処理のど定番のsidekiq。
それをActiveJob経由で使っている場合は、sidekiq_optionsが使えないので細かな設定ができない。
今回、sidekiqにするにしたがってリトライは現時点では全てのジョブで不要なので、リトライしないようにしたかった。
結論としてはconfig/initializers/sidekiq.rbにSidekiq.options[:max_retries] = 0
を設定してあげればいい。
sidekiqのwikiでこの設定見当たらなかったので、ソース調べたら見つけた。
https://github.com/mperham/sidekiq/blob/v5.1.3/lib/sidekiq/job_retry.rb#L46-L49
デフォルトが25らしいので、それを変更する際にもこの設定方法でいいと思われる。
Capybara::Poltergeist::StatusFailErrorが出たら
このエラーはローカルで
Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: 1)
のようにpoltergeistのtimeoutをすごく短くすると同様のエラーになる。
原因はassets compileに時間がかかっていてタイムアウトしている説らしい。
とりあえず、timeoutを伸ばして様子をみて、ダメなら先にassetファイルにアクセスするやテスト前にprecompileをするなどの方針を考える必要がある。
Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: 60) end
Capybara::Poltergeist::StatusFailError · Issue #791 · teampoltergeist/poltergeist · GitHub
Capybara::Poltergeist::TimeoutErrorが稀に起こる問題の対応 - Qiita
Timeout::Error: execution expiredの場合
このエラーの場合はどのgemがというよりはテスト内にTimeout.timeout(5)
とかやっている場合がある。
自分の場合はajaxの処理が完了するまで待つみたいなやつがあった。
Aurora MySQLのmax_connectionsの設定
テスト環境でrailsのdatabase.ymlのpool設定を本番に合わせたいが、Auroraの方のmax_connectionsが45になっていた。
これでは足りないので45より多くしたい。
まず、Auroraのmax_connectionsはインスタンスタイプでデフォルトが決まるような設定がされている。
なので、これを変更する。
そのために、AWSのコンソールからRDS→パラメーターグループから対象RDSに対応するパラメーターグループを選択して、フィルターでmax_connectionsで検索して"パラメーターの編集"を押して変更すればOK。
特に RDSの再起動をしなくても設定が適応されている。(適用タイプがdynamicなので)
※mysql> SHOW VARIABLES LIKE 'max_connections';
で確認