Tech Hotoke Blog

IT観音とは私のことです。

スッキリ!Rubyコード改善術: おしゃれにHashを追加してみた #Ruby #Ruby on Rails #Rails

これは何?

条件によってHashにプロパティを追加したい場合に、一行一行追加する記述が冗長だったので、もう少しRubyっぽく書けないか施工錯誤したメモ。 結論はtapを使うといい感じだった。(サンプルの例がダサいコードの方がスッキリ見えてしまい申し訳ないですが...)

前提

Tips

  • tapはself を引数としてブロックを評価し、self を返します。メソッドチェインの途中で直ちに操作結果を表示するためにメソッドチェインに "入り込む" ことが、このメソッドの主目的です。 とあります。この説明だけではピンときていませんでした。
  • しかし、今回のようなケースで使うと、ブロックを渡すことができるため、条件分岐などのロジックを含めてオブジェクトを操作することができて柔軟にコードが書けるという気付きを得ました。
  • Hashオブジェクト自身にチェーンして記述するのでコードが一つの塊として読めるのも個人的にはわかりやすくていいなと感じました。Hashのプロパティ追加操作がHash本体と離れているとコードを読む時にストレスに感じるので。

Object#tap (Ruby 3.3 リファレンスマニュアル)

  • ダサいコード
  def hoge(flag)
    hash = {  fuga: false  }

  // その他の処理
  // その他の処理
  // その他の処理
  // その他の処理

   hash[:piyo] = "Piyo" if flag
   hash[:fuga] = "Fuga" if flag
   hash[:hogehoge] = "hogehoge" if flag
  end
  • おしゃれなコード
  def hoge(flag)
    {
      fuga: false
    }.tap do |hash|
      if flag
        hash[:piyo] = "Piyo"
        hash[:fuga] = "Fuga"
        hash[:hogehoge] = "hogehoge"
      end
    end
  end

備考

  • 実際のところ、どちらが良いのだろう? 好み?

デバッグ方法:AWS ECSタスク更新時のトラブルシューティング

これは何?

AWS ECSのタスクがデプロイ時に正常に更新されなかった場合のデバッグ方法のメモ

前提

AWS Pipeline, CodeBuild, ECSを使用していること

Tips

  • Pipeline・CodeBuildともにステータスに異常はない
  • ECSのタスクを確認すると、タスクが更新されていない
  • どうする?

  • A:

    • イベントタグを確認する
    • イベントタグのログにdeployment failed: tasks failed to start. 的な文言が出ている。
    • 直下にfailしたタスクのIDリンクが存在するので、そちらに遷移
    • 遷移先のページに画像のような原因が書かれている(今回はパラメータストアに設定したパスとデプロイされたコードの参照しているパスが異なっていたことが原因だった)

備考

  • ECSタスクが更新されなかったら、Slackなどに通知するみたいなことができるか調べてみる

useEffect × ReactQueryを使った、処理実行順序の制御

これは何?

カスタムフックの処理が実行される前に、useEffectの処理を先に行いたいケースが出てきた。その際にすぐに解決方法がわからなかったので備忘録として書く。

前提

  • React18
  • TypeScript: 4.7.4
  • ReactQuery
  • ReactHookForm

Tips

  • ケース

    • 画面遷移時にuseFuga()を使用してPOSTリクエストが走る。
    • useFuga()はReactQueryで実装されたカスタムフック。
    • POSTリクエストの前に、POSTするdataの値を更新したい。
    • しかし、useEffect()はカスタムフックの後に実行される。
    • どうする???
  • A:

    • useStateを作成
    • useEffectの中でstateの更新
    • stateの値をReactQueryのconfig:{enabled: }に設定する
    • これで、POSTするdataの値を更新した後にカスタムフックの呼び出しを行うことができる。
サンプルコード
  const [isUpdated,  setIsUpdated] = useState(false)

  useEffect(() => {
    const fields = getValues('hoge')
    const updatedFields = fields.map((field, index) => ({
      ...field,
      position: index + 1
    }))

    setValue('hogeAttributes', updatedFields)
    setIsUpdated(true)
  }, [])

  const { data, isLoading, isError, isSuccess } =
    useFuga({
      params: {
        data: getValues()
      },
      config: {
        enabled: isSetPinned
      }
    })

AWS Lambda 入門

これは何?

AWS Lamdaを触ることになった。とりあえず、なんとなくの雰囲気で実装してみたところ、かなり初歩の段階でエラーが発生した。 この機会なので、学習を兼ねて、まずはちゃんとドキュメントを読んでからエラーの解消にあたって行こうと思い書いてます。

前提

  • Rubyで実装しているので、Rubyベースで。
  • コードは実務で使っているものになってしまうので、内容を改変・抜粋して掲載しています。(コピペでは動かないと思ってください)
  • 公式リファレンスを網羅しているわけではありません。
  • AWS SESでLambdaを使用することを前提にしています

AWS Lambdaって何?

  • AWS Lambda はサーバーのプロビジョニングや管理をする必要がなく、コードを実行できるコンピューティングサービスです。
    • いわゆるサーバーレスアーキテクチャというやつですかね。Lambdaの他に、グーグルの「Google Cloud Functions」、IBMの「OpenWhisk」、マイクロソフトの「Azure Functions」などが挙げられますが、定義がそれぞれ異なるため一概にサーバーレスという言葉でくくることができないのが注意。
    • AWSにおけるサーバーレスは「インスタンスベースの仮想サーバー(EC2など)を使わずにアプリケーションを開発するアーキテクチャ」と個人的には解釈しています(間違っていたらご指摘ください)
    • 必要な場合にのみ関数を実行する
    • 自動的にスケーリング
    • 従量課金
    • フルマネージドサービス(コードを実行するメモリのバランス、CPU、ネットワーク、その他のリソースを提供するコンピューティングフリート)
    • コンピューティングインスタンスにログインしたり、提供されたランタイムのオペレーティングシステムをカスタマイズしたりすることはできない → コンピュートリソースを管理する必要がある場合は向かない

Lambdaの概念

  • 関数:Lambda でコードを実行するために呼び出すことができるリソース -トリガー:Lambda 関数を呼び出すリソースまたは設定
  • イベント:処理する Lambda 関数のデータを含む JSON 形式のドキュメント。ランタイムによりオブジェクトに変換された上で、関数のコードに渡される。
  • ランタイム環境:関数の実行に必要なプロセスとリソースが、実行環境により管理される。
  • 命令セットアーキテクチャ:Lambda が関数の実行に使用するコンピュータプロセッサタイプ。2024/02/27時点では、arm64かx86_64が選択できる。
  • レイヤー:追加のコードまたはデータを含むことができる .zip ファイルアーカイブ。レイヤーには、ライブラリ、 カスタムランタイム 、データ、または設定ファイルを含めることができる。

    • 各関数につき 最大 5 つ のレイヤーを含めることができる
    • 関数にレイヤーを含むと、実行環境においてコンテンツが /opt ディレクトリに抽出される。
    • デフォルトでは、作成したレイヤーは AWS アカウントに対してプライベートになる。
    • コンテナイメージとしてデプロイされた関数はレイヤーを使用しない。代わりに、コンテナイメージをビルドする際、必要なランタイム、ライブラリ、およびその他の依存関係を、そのイメージ内にパッケージ化する。
  • 拡張機能

    • 関数を拡張できる。
      • 例) 任意のモニタリングツール、オブザーバビリティツール、セキュリティツール、およびガバナンスツールに関数を統合できます。
    • AWSか提供するツールセットが存在する。カスタムで作成も可能。
    • 拡張機能は内部モードと外部モード、2つのタイプに分かれるらしい。
    • 内部拡張機能は、ランタイムプロセスで実行され、ランタイムと同じライフサイクルを共有する。
    • 外部拡張機能は、実行環境で別のプロセスとして実行される。外部拡張機能は、関数が呼び出される前に初期化される。また、関数のランタイムと並行して実行され、関数の呼び出しが完了した後も引き続き実行される。
  • 同時実行

    • ある時点で関数が処理しているリクエストの数。
    • リクエストの処理中に関数が再度呼び出されると、別のインスタンスがプロビジョンされるため、関数の同時実行数が増加する。
  • エイリアス

    • 関数を呼び出したり表示したりするときに、バージョンまたはエイリアスを指定するための修飾子を含めることができる。
    • バージョンは、数値修飾子を持つ関数のコードと設定の変更不可能なスナップショット。たとえば、my-function:1 と指定できる。
  • 送信先

    • Lambda が非同期呼び出しからイベントを送信できる AWS リソース。
    • 処理に失敗したイベントの送信先を設定できます。一部のサービスでは、正常に処理されたイベントの宛先もサポートしている。

今回発生したエラーと対応

{
    "errorMessage": "cannot load such file -- lambda_function",
    "errorType": "Init<LoadError>",
    "stackTrace": [
        "<internal:/var/lang/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:86:in `require'",
        "<internal:/var/lang/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:86:in `require'"
    ]
}

lambda_functionが存在しないとのことでしたが、Handler設定でデフォルトがlambda_function として設定されていました。 私のコードではルートディレクトリにhandler.rbファイルを作成し、notifyメソッドを定義していたため、添付画像のような形式になるのが正でした。

AWS SESからどんなデータが渡ってくるのか?

{
  "Records": [
    {
      "eventVersion": "1.0",
      "ses": {
        "mail": {
          "commonHeaders": {
            "from": [
              "Jane Doe <janedoe@example.com>"
            ],
            "to": [
              "johndoe@example.com"
            ],
            "returnPath": "janedoe@example.com",
            "messageId": "<0123456789example.com>",
            "date": "Wed, 7 Oct 2015 12:34:56 -0700",
            "subject": "Test Subject"
          },
          "source": "janedoe@example.com",
          "timestamp": "1970-01-01T00:00:00.000Z",
          "destination": [
            "johndoe@example.com"
          ],
          "headers": [
            {
              "name": "Return-Path",
              "value": "<janedoe@example.com>"
            },
            {
              "name": "Received",
              "value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.us-west-2.amazonaws.com with SMTP id o3vrnil0e2ic for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)"
            },
            {
              "name": "DKIM-Signature",
              "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=example; h=mime-version:from:date:message-id:subject:to:content-type; bh=jX3F0bCAI7sIbkHyy3mLYO28ieDQz2R0P8HwQkklFj4=; b=sQwJ+LMe9RjkesGu+vqU56asvMhrLRRYrWCbV"
            },
            {
              "name": "MIME-Version",
              "value": "1.0"
            },
            {
              "name": "From",
              "value": "Jane Doe <janedoe@example.com>"
            },
            {
              "name": "Date",
              "value": "Wed, 7 Oct 2015 12:34:56 -0700"
            },
            {
              "name": "Message-ID",
              "value": "<0123456789example.com>"
            },
            {
              "name": "Subject",
              "value": "Test Subject"
            },
            {
              "name": "To",
              "value": "johndoe@example.com"
            },
            {
              "name": "Content-Type",
              "value": "text/plain; charset=UTF-8"
            }
          ],
          "headersTruncated": false,
          "messageId": "o3vrnil0e2ic28tr"
        },
        "receipt": {
          "recipients": [
            "johndoe@example.com"
          ],
          "timestamp": "1970-01-01T00:00:00.000Z",
          "spamVerdict": {
            "status": "PASS"
          },
          "dkimVerdict": {
            "status": "PASS"
          },
          "processingTimeMillis": 574,
          "action": {
            "type": "Lambda",
            "invocationType": "Event",
            "functionArn": "arn:aws:lambda:us-west-2:111122223333:function:Example"
          },
          "spfVerdict": {
            "status": "PASS"
          },
          "virusVerdict": {
            "status": "PASS"
          }
        }
      },
      "eventSource": "aws:ses"
    }
  ]
}

参考: Amazon SES で AWS Lambda を使用する - AWS Lambda

AWS Lambdaにレイヤーを作成する

coming soon...

AWS IAM インラインポリシーの設定方法

これは何?

インラインポリシーの設定方法を忘れてしまった AND 検索してもすぐに見つからなかったので備忘録として

設定方法

  1. 任意のロールを作成する
  2. 任意のロールを選択する
  3. 許可タグの許可ポリシーから、インラインポリシーを作成を選択
  4. 任意のポリシーを作成する

AWS SNSとSQSの連携についての学習メモ

これは何?

AWS SNS, SQSの連携周りを触ることになったので、その際の学習メモ。

AWS SNSって何?

  • Amazon Simple Notification Serviceの略
  • 配信者から受信者へのメッセージ配信を提供するマネージドサービス
  • 論理アクセスポイントおよび通信チャネルであるトピックという単位で管理される
  • 非同期的に通信する
  • A2A(aplication-to-application) メッセージングとA2P(aplication-to-person)通知が可能

A2Aメッセージング

  {
   "Type" : "Notification",
   "MessageId" : "63a3f6b6-d533-4a47-aef9-fcf5cf758c76",
   "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
   "Subject" : "Testing publish to subscribed queues",
   "Message" : "Hello world!",
   "Timestamp" : "2012-03-29T05:12:16.901Z",
   "SignatureVersion" : "1",
   "Signature" : "EXAMPLEnTrFPa3...",
   "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
   "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c7fe3a54-ab0e-4ec2-88e0-db410a0f2bee"
}

A2P(aplication-to-person)通知

標準および FIFO トピック

  • FIFO トピック:メッセージの順序を厳格に保つ。メッセージグループを定義し、メッセージの重複を防止する。FIFO トピックにサブスクライブするには、FIFO キューと標準キューの両方を使用可能。(AWS SESと連携する場合は2024/02/27時点ではサポートされていないので注意)
  • 標準トピック:メッセージの配信順序やメッセージの重複が重要でない場合。サポートされているすべての配信プロトコルは、スタンダードトピックにサブスクライブ可能。

メッセージの耐久性

  • 公開されたメッセージは、地理的に分離された複数のサーバーおよびデータセンターの間で保存されます。
  • サブスクライブされたエンドポイントが利用できない場合、Amazon SNS は配信の再試行ポリシーを実行します。
    • 配信の再試行ポリシー:サブスクライブされたエンドポイントをホストするシステムが利用できなくなったときにメッセージの配信を再試行する方法
    • 配信ポリシーが枯渇すると、Amazon SNS は配信の再試行を停止し、 メッセージを破棄 する (デッドレターキューが添付されていれば破棄されない)

    • 配信ポリシーの段階

      1. 即時の再試行段階(遅延なし) - 最初の配信の試行の直後に発生。この段階では再試行間の遅延時間はない。
      2. バックオフ前段階 - 即時の再試行段階の後。Amazon SNS は、バックオフ関数を適用する前に、この段階を使用して一連の再試行を試みる。この段階では、再試行回数と再試行間の遅延量を指定する。
      3. バックオフ段階 - この段階では、再試行バックオフ関数を使用して、再試行間の遅延を制御する。この段階では、最小遅延、最大遅延、および遅延が最小遅延から最大遅延までどれだけ速く増加するかを定義する再試行バックオフ関数を設定する。
      4. バックオフ後段階 - この段階はバックオフ段階の後。再試行回数とその間の遅延量を指定する。

デッドレターキュー

  • Amazon SNS サブスクリプションが受信者に正常に配信できないメッセージの送信先としての Amazon SQS キュー
  • どんなときにメッセージの配信が失敗する?
    • クライアントサイドの原因
      • 所有者がエンドポイント (Amazon SNS トピックにサブスクライブされている Lambda 関数など) を削除した場合
      • サブスクライブしているエンドポイントに添付されているポリシーを、Amazon SNS がエンドポイントにメッセージを配信できないように所有者が変更した場合
    • サーバーサイドの原因
      • サブスクライブされたエンドポイントを担当するシステムが利用できなくなった場合
      • Amazon SNS からの有効なリクエストを処理できないことを示す例外を返す場合
  • サーバー側のエラーが発生するとどんな挙動をする?

    • サーバー側のエラーが発生すると、一次バックオフ関数またはエクスポネンシャルバックオフ関数を使用して、失敗した配信を再試行する。
    • Amazon SQS または AWS Lambda によってバックアップされた AWS 管理のエンドポイントに起因するサーバー側エラーについては、Amazon SNS 23 日間にわたって 100,015 回まで配信を再試行する (そうなんだ。。。)
    • カスタマー管理のエンドポイント (HTTP、SMTP、SMS、モバイルプッシュなど) では、SMTP、SMS、およびモバイルプッシュエンドポイントに対して、内部配信再試行ポリシーを 6 時間にわたって 50 回に設定する。
  • デッドレターキューのしくみ

  • メッセージをデッドレターキューから移動する方法

    • Amazon SQS コンシューマロジックの作成を避ける
      • デッドレターキューをイベントソースとして Lambda 関数に設定して、デッドレターキューを吸い出します。
    • Amazon SQS コンシューマロジックを作成する
      • Amazon SQS APIAWS SDK、または AWS CLI を使用して、デッドレターキュー内のメッセージのポーリング、処理、削除を行うカスタムコンシューマーロジックを記述します。
  • デッドレターキューのモニタリングとログ記録方法

メッセージのアーカイブ、リプレイ、および分析

coming soon

メッセージ属性

coming soon

メッセージのフィルター処理

coming soon

メッセージセキュリティ

coming soon

【Cypress】cy.get()とcy.contains()の違い

前提

  • Cypress: 13.0.2

  • 以下のコードの違いは何でしょうか?
cy.get('tr:contains(User 1)')
cy.contains('tr', 'User 1')
  • こんな違いがある

    • cy.get()は一つ以上のDOM Elementを取得する
    • cy.contians()は一つのDOM Elementを取得する
  • つまり、以下のようになる。

cy.get('tr:contains(User 1)') // User 1を含む,table要素のtrを全て取得する
cy.contains('tr', 'User 1') // User 1を含む,tableからUser 1 の trを一件取得する

実際の挙動の確認や、なぜこのような動きになっているかの検証、じゃあ使い分けはどうすんの?な話は気が向いた時にやります🙏