パパエンジニアのアウトプット帳

30歳に突入した1児のパパエンジニアのブログ

RubyMineでSlim+Vue.jsを利用していると開けなくなるファイルの条件が判明した

環境

  • macOS High Sierra 10.13.6
  • RubyMine 2018.2.4 Build #RM-182.4505.55, built on October 8, 2018


今の会社になってからRubyMineも会社で購入してくれるのでバリバリ使っているのですが、ジョインした時からいくつかのslimファイルがRubyMineで開けない状態だった。(VSCodeなどでは開ける)

前からいる人もなんでそのファイルたちだけが開けないのか不明で、しかも検索の対象にもならないのでそのあたりの画面を触る時にとても困っていた。


VSCodeに乗り換えるか?という話も出てきだしたが、一度RubyMineの便利さ(特にコードジャンプ)を知るとなかなか他では代替できないと自分は思っている。(VSCodeでslimファイル内でrubyのコードなどにコードジャンプできるようにする方法を知っている人がいたら是非教えて欲しい)


そんな折、ひょんな事から自分がそのRubyMineで開けないファイルを生み出してしまったので、色々試してみるとどうやらSlim内でVue.jsのv-bindの省略記法を書いているものがあると開けなることが分かった。

my-component [
  v-for = "item in list"
  track-by = "uuid"
  :item = "item"
]

このような書き方がSlim内に含まれると、RubyMineで開けなくなる(ファイルのタブは出るがずっとloading…のままになる)

なので、v-bindを省略記法ではなくちゃんと書けば大丈夫。

my-component [
  v-for = "item in list"
  track-by = "uuid"
  v-bind:item = "item"
]

今回みたいに属性が3つくらいだと[]をあまり使わないかもしれないが、属性が多くなってくるとこっちの方が見やすくなる。(ちなみにこの書き方は最近まで知らなかった。。。)


省略記法を書けないと不便に感じるかもしれないが、自分は省略記法はRails + Vue.jsでは混乱させると思っているので会社のプロジェクトでは使わないように提言しているのだが、より強く言っていこうと思った。 (:item = itemと書かれたら一瞬RubyのHash?って思いません?あと@clickとか)

webpackのProvidePluginとresolve.aliasの使い分け

webpackのProvidePluginとresolve.aliasの使い分けがなんとなく分かったような気がするのでメモ。


vue.jsについて調べていた時に両方の書き方をした記事が引っかかってどっちでやったら良いか分からずモヤモヤしていた。

ProvidePlugin

new webpack.ProvidePlugin({
    Vue: ['vue/dist/vue.esm.js', 'default'],
  })

こうすると、変数として利用可能になるのでrequireやimportせずに使える。

// import Vue from 'vue' ←不要!!

new Vue({....})

resolve.alias

resolve: {
        alias: {
            vue: 'vue/dist/vue.esm.js',
        }
    }

これは、Vue.jsでES Moduleビルドを利用する時に

import Vue from 'vue/dist/vue.esm.js'

と毎度書くのが面倒なので、上記のaliasを設定すると

import Vue from 'vue'

でvue/dist/vue.esm.jsを参照したのと同じになる。

結局どう使い分けるか?

今回のVue.jsの場合はどちらもvue/dist/vue.esm.jsを気にしなくてよくなるが、ProvidePluginを利用した方がimportも都度書かなくてもよくなるのでこっちが良さそう。


aliasは自分のコンポーネントとかで

import Hoge '../../components/hoge.js'
import Fuga '../../components/fuga.js'

とかしている時に威力を発揮しそう。


[2018/11/09 追記]

resolve.aliasは外部ライブラリでVue.jsに依存している場合に必要。

ライブラリ内に import Vue from 'vue'があるとそれはVue.js2系だとvue.runtime.esm.jsになってしまうので、それではマズイ場合にresolve.aliasを指定して強制的に上書きする時に使うので、使い分けと言うよりか両方使う感じになるのかな?


webpack完全に理解した!までの道のりはまだ遠い。。。

初gemを作成した

Logrageを利用していてActiveJobのログも出力したかったのでgemを作った。

github.com

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対応した感じ。

docs.aws.amazon.com

次のオペレーションでは、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

下記の記事も詳しくてよかった。

takatorix.hatenablog.com

クラスターモード有効時

検証用にクラスターモードの場合も構築してみた。

  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で設定しましょう。

www.kabegiwablog.com

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らしいので、それを変更する際にもこの設定方法でいいと思われる。