perform_nowだとquery cacheが効かない?が、perform_laterだと効く

会社のslackの分報でrailssqlの結果をメモ化するにはどんな方法がいいんだ?

みたいなことを呟いたら、そもそもquery cacheが効くんじゃない?と言われましたが、

https://railsguides.jp/caching_with_rails.html#sql%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5

railsガイドでも↑のような書き方で、Active Jobには関係ないのかとずっと思ってました。

ただ、perform_laterだとquery cacheが効くという助言をいただいたので、検証して見ることに。

https://github.com/na8esin/rails7_mysql_docker/tree/21630b376013d0059daab1553760bb15f4eb636b

rails7 + aws-sdk-rails + mysql8 + localstack(sqs)の組み合わせです。

まず、jobのコードはこんな感じです

class BookJob < ApplicationJob
  queue_as :default

  def perform
    Book.find(1)

    Book.find(1)
  end
end

perform_nowの結果です。

irb(main):002:0> BookJob.perform_now
Performing BookJob (Job ID: de1e614d-42d8-4568-9766-f38218dfac4c) from AmazonSqs(default) enqueued at 
  Book Load (3.9ms)  SELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1
  Book Load (0.2ms)  SELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1
Performed BookJob (Job ID: de1e614d-42d8-4568-9766-f38218dfac4c) from AmazonSqs(default) in 81.96ms
=> 
#<Book:0x0000ffff83294108
 id: 1,
 status: "available",
 created_at: Tue, 12 Sep 2023 10:05:54.264743000 UTC +00:00,
 updated_at: Tue, 12 Sep 2023 10:05:54.264743000 UTC +00:00>

ログにはCACHEとはでてないですね。でも2回目は3.7msも早い。本当に効いてないのか? ちょっと不安になったので、タイトルにも?がついてます。

次は perform_laterです。

まずは、rails cでBookJob.perform_laterを実行しておきます。

そのあと、rails cを抜けてから、RAILS_ENV=development bundle exec aws_sqs_active_job --queue defaultを実行して、少々待つと下記のようなログがファイルに出力されます

[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5] Performing BookJob (Job ID: b8cd5b55-1d44-420d-bfe5-89b7c67be8d5) from AmazonSqs(default) enqueued at 2023-09-14T10:28:21Z
[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5]   [1m[36mBook Load (0.1ms)[0m  [1m[34mSELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1[0m
[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5]   ↳ app/jobs/book_job.rb:8:in `perform'
[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5]   [1m[36mCACHE Book Load (0.0ms)[0m  [1m[34mSELECT `books`.* FROM `books` WHERE `books`.`id` = 1 LIMIT 1[0m
[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5]   ↳ app/jobs/book_job.rb:10:in `perform'
[ActiveJob] [BookJob] [b8cd5b55-1d44-420d-bfe5-89b7c67be8d5] Performed BookJob (Job ID: b8cd5b55-1d44-420d-bfe5-89b7c67be8d5) from AmazonSqs(default) in 20.99ms
[Aws::SQS::Client 200 0.009193 0 retries] delete_message(queue_url:"http://localstack:4566/000000000000/my-queue",receipt_handle:"MWM5Mzg3MjktZmM2Yy00YzcyLTgxMDktZWMwMDNlZGFmMTUwIGFybjphd3M6c3FzOmFwLW5vcnRoZWFzdC0xOjAwMDAwMDAwMDAwMDpteS1xdWV1ZSBhNGRlMWQyMy1lOWZiLTQzZWUtYjcxYS01MzE1Nzc0OGRjMTkgMTY5NDY4NzMyNS44OTY2MTA1")  

CACHEと出てますね。

ただ、この例だと、ログにCACHEと出ているだけなのか、本当にCACHEが効いてるのかはわかりづらいですね。

時間がある時にrails本体のソースを読んでみたいと思います。

RemoteConfig使ってみる(swift)

アプリには欠かせないapi呼び出しですが、urlのドメインだけを変えたい場合があると思います。

まずは、iosアプリとして、サンプルを作ってみました。

https://github.com/na8esin/ios-remote-config

AppDelegateでRemoteConfigのsetupを行うサンプルになってます。

また、structにURLなどの定数を押し込めてるプロジェクトもよくあると思いますので、 そこで、RemoteConfigを呼び出すようにしてみました。

RemoteConfigで取得した値をUIに反映させるのは、とりあえずボタンで行っています。

本当は、ListenerがRemoteConfigの更新を検知したタイミングで、UIを更新したり、 splashの画面に遷移したいところです。

FirebaseApp.initializeApp不要らしい

Androidのプロジェクトで、Remote Configの導入を検討しており、まずは下記のquickstartをダウンロードして、実行してみることに

https://github.com/firebase/quickstart-android/tree/master/config

java.lang.RuntimeException: Unable to start activity ComponentInfo{ta.watanabe.practice/com.google.firebase.quickstart.config.java.MainActivity}: java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process ta.watanabe.practice. Make sure to call FirebaseApp.initializeApp(Context) first.

FirebaseApp.initializeApp(Context)かあ、そういえば必要だったなと思って、追記してみるも同じ結果。。。

そして下記に辿り着きました。

https://github.com/firebase/firebase-android-sdk/issues/4693#issuecomment-1674688652

Added apply plugin: 'com.google.gms.google-services' to app/build.gradle. Also added classpath 'com.google.gms:google-services:4.3.4' to build.gradle

上記の通りに追記すると動くようになりました。

AWS Lambdaをnodejs18.xで作成して、コードを管理しようとした時にハマったこと

まず、Serverless Frameworkでコード管理をしようと思いました。

nodejs18.xを選択すると拡張子がデフォルトで.mjsになっていますが、まだその拡張子に対応してないようでした。

https://github.com/serverless/serverless/pull/11366

拡張子を.jsにしてES モジュールとして扱う方法もありますが、

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-nodejs.html

他の人が見た時にわかりづらいと思ったので、Serverless Framework自体の利用を一旦、諦めました。

そうなるとTerraformで管理しようかと思って、tfファイルをある程度書いて、planを実行したところ下記のエラーに遭遇しました。

https://github.com/hashicorp/terraform-provider-aws/issues/27878

こちらは、すでに対応されていたので、providerのバージョンを上げれば解決しました。

Alpine Linuxでもaws cli v2がapk addでインストールできる

普通にpackagesに載ってる

https://pkgs.alpinelinux.org/packages?name=aws-cli&branch=edge&repo=&arch=&maintainer=

↓でも言及されている

https://github.com/aws/aws-cli/issues/4971#issuecomment-1631160032

dockerで試してみた

aws cliはterragruntと一緒に使いたくなる場合が多いと思うので、alpine/terragruntのimageで試してみました(m1 macです)。

とりあえずは、問題なさそう。

Android, iOSのアプリで使用しているTwitterのAPIがついに使えなくなった

仕事でメンテナンスしているアプリのTwitter連携の機能が突然使えなくなったので、調べました。

世間で使えなくなったと言われているよりタイムラグがあったのと、Android, iOSで使えなくなった日がそれぞれ違く、数日ラグがありました。

多分、Android, iOSで使ってるConsumer Keyがそれぞれ違うからだと思われます。

動作確認にはtwurlが一番簡単そうだなと思って、まずはインストールしました。

Twurlを使用する | Docs | Twitter Developer Platform

そして上の記事通りauthorizeします。

そして、該当のapiを実行。

$ twurl /1.1/users/show.json  
{"errors":[{"message":"You currently have access to a subset of Twitter API v2 endpoints and limited v1.1 endpoints (e.g. media post, oauth) only. If you need access to this endpoint, you may need a different access level. You can learn more here: https://developer.twitter.com/en/portal/product","code":453}]}

https://twittercommunity.com/t/yesterday-everything-was-fine-now-you-currently-have-access-to-a-subset-of-twitter-api-v2-endpoints-and-limited-v1-1-endpoints-e-g-media-post-oauth-only/196198

同じエラーに出会した人もいるようです。

v2を使えるようにすればいいんですが、アプリが2つ(Android, iOS?)あると、有料だそうなので、まだどうするかの結論が出ていないです。

content_tag(divタグ)になったりlink_toになったりするヘルパー

詳しい要件は忘れてしまいましたが、htmlタグの要素だけ違くて、他はほぼ同じみたいなことをすることがあります。

その場合もerbで分岐処理を書かずに、blockを使ってhelperに書くとスッキリする気がしました。

module SomeHelper
  def content_tag_or_link_to(path, class:, is_content_tag: false, &block)
    if is_content_tag
      content_tag :div, class:, &block
    else
      link_to path, class:, &block
    end
  end
end
<%= content_tag_or_link_to some_path, class: "btn", is_content_tag: @is_content_tag do %>
  <div class="label">cssによりボタンの見た目をしています</div>
<% end %>