プロセスもプロダクトも

こんにちは 今回はただの日記です

ある日突然、あなたにプロダクトの大きい方向性をゆだねられたときどうしますか? それも……とびっきりざっくりとした指定でいい感じの機能を考えろと言われた場合。。。

そんなわけで本を注文しました

www.amazon.co.jp

おわり

以下雑文

IT関連エンジニアとして働いている場合、 基本的にはプロセスだったり方法論、スケジューリングについて責任を持つことが多いのかなと思います。 業務で直面する課題も技術的な要素であれば、技術書なりインターネット上にいくらでも類似の事例やベストプラクティス等の知見が共有されていますから、 根気と着実な推論があればある程度正解に近づける気がします。

一人で完結する部分がある+素早くフィードバックを得られる点もいいですよね。

一方でプロダクトの機能を改善でなく1から考える必要がある場合は、それらプロセスのためのエンジニアリングスキルとは違った別のスキルが必要になります。 プロダクト=ビジネスをするということなので、コミュニケーションなり企画提案なり、ソフトスキルをより意識していく必要があります。 加えてビジネスの場合はベストプラクティスもあるか怪しいですし、 インターネット上には怪しい業者や成功者バイアスのかかった情報しかないといっても過言ではありません。

www.geeksforgeeks.org

そもそもプロダクトマネージャがエンジニアのキャリアの延長戦にあるのか?ということなのですが、 製品、人によりけりなのかなと思います。 上の記事は英語圏のCSコミュニティの影響力のあるサイトですが、 まあCSだけやっている人がすぐなる選択肢ではないのかなと思います。

ただGoogle等トップ企業はCSの優秀な人をPdMとして採用しているようですね。

プロダクトマネージャーの教育プログラム: APM/RPM(雑文). ここ数年、各社でプロダクトマネージャーへの注目度が高まっているのか、プロダクトマ… | by Taka Umada | Medium

APM研修非常に気になりますね ユーザのニーズや市場の様子見て戦略を決めるプロセス等が伝授されているのでしょうか

まあプログラマ起業家精神があれば何でもできるような職業でもあるので、 非ITの技術職と比べるとビジネスを考えるのに近い立場にいる気もします。

アイカツに学ぶ

昔からちょくちょくアイカツをお勧めされるんですが(部分的にしか見てない)、 たまたまよさげな名言がTLに流れてきたので下に張っておきます。

altair GraphQL client のプラグインを作る

みなさんGraphQL開発していますか?

個人的に開発時のローカルクライアントとしてお勧めしているのがAltair GraphQL Clientです

altair.sirmuel.design

GraphiQLとかも選択肢とはありますが、それに比べると ・コレクション機能(リクエストをエクスポートして共有できる) ・pre/post script機能(変数/ローカルストレージの値を利用してリクエスト前にヘッダを設定するようなスクリプトが動かせる) というような機能がありチーム開発だと便利だったりします。

今回はこのaltairのプラグインを作ってみます

viteで設定

今回はviteで作成します。 いつものコマンドでプロジェクトフォルダを作成します。(react+ts)

npm create vite@latest

なお今回はSPAではなく

Writing A Plugin | Altair GraphQL Client

にあるようなプラグインスクリプトがゴールのため、成果物/エントリポイントがhtmlではないです。

vite.config.tsを適切に記述することでビルド成果物ファイル名の設定と、エントリポイントの設定を行います。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'


// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'src/index.ts'), //成果物名
      },
      output: {
        entryFileNames: `index.js`, //エントリポイント
      },
    }
  }
})

altairの設定:plugin manifestについて

altairプラグインのnpmモジュールはルートフォルダにmanifest.jsonに独自のフォーマットのjsonを記述する必要があります。

https://altair.sirmuel.design/docs/plugins/writing-plugin.html

上記ページに入れるべき設定が記載されています。 今回は以下のようにしました。

{
    "manifest_version": 1,
    "name": "altair-graphql-plugin-[名称]",
    "version": "0.0.1",
    "display_name": "[表示名称]",
    "description": "[説明]",
    "type": "header",
    "sidebar_opts": {
        "element_name": "[nameと同じ]",
        "icon": "assets/icon.svg"// アイコン
    },
    "scripts": [
        "dist/index.js"//読み込みスクリプト
    ],
    "plugin_class": "AltairPluginName" //任意の名称
}

このファイルでplugin_classで指定したウィンドウのプロパティをプラグインとして動作させるという設計のようです (以下コード抜粋)

class AltairPluginName {
    initialize(ctx:any) {
...
    }
    async destroy() {}
}
export {}

// Add the class to the Altair plugins object
(window as any).AltairGraphQL.plugins.AltairPluginName = AltairPluginName;

なお他にもnpmモジュール名をaltair-graphql-plugin-から始めるように等細かい規約/制約が存在します。 公式の説明を読んで適宜設定しましょう。

完成したのがこちらです

https://github.com/osjupiter/altair-graphql-plugin-cognito-auth

クリックすると数字が増えるカウンタがウィンドウに増えました。 やったね

本来はこの後に altair.sirmuel.design

に記載があるように,

  • ctx.app.createAction
  • ctx.events.on(evt, callback)

のようなAPIを利用して、作成したいプラグインの動作を記述していくことになります。

おまけ: バンドル後のサイズについて

今回の成果物のjsファイルのサイズは114kbでした。 バンドル後のサイズが気になる場合はpreact/solidjsあたりを使用するのが良さげっぽいですね? vanilajsでも頑張れば作成できると思うので、機会があれば試してみようと思います。 そもそもパネルを既存の画面に追加するくらいのため、altairプラグイン開発ではUI関連ライブラリのが充実していないツールセットでも 問題なく採用できるかと思います。

おまけ: vite

Vite Plugin Node で Vite を Node.js Webアプリケーションの開発でも利用する - Webdelog

に記述があるんですが、どうもviteにはなぜか(サーバ側の)webアプリケーションをhmrする変態機能がついているみたいです。 すごいですね

今後の予定

チームではAWS AppSyncを使いgraphQLのAPIを公開しているのですが、 認証にcognitoを紐づけています。 この紐付けを行うと、リクエストのAuthorizationヘッダにcognitoのアクセストークンを設定しないとリクエストが弾かれるようになります。

この後はプラグインでaltairからcognitoにログインできるようにしてみる予定です

最近XP-PENのペンタブを買った

題名の通りです

買ったのは以下です

www.xp-pen.jp

もともとwacomの古いペンタブを持っていたのですが、 もうすでに販売されていない機種だったのと、 地味にペンタブを使いたいときにいちいちケーブルをつなぐ/使わないときにしまうという作業をするのがめんどくさかったんですよね

せっかくなので描いたイラストを貼っておきます!!!

skebで頼んでから自分で胡桃の絵を描いたので、実質依頼料5000円分得した気がしますね。

生産していけ!!! (金銭のやり取りが発生しないためGDPには寄与しない、むしろにGDP寄与する必要はあるか?)

denoでドドスコ=デノスコスコスコ

タイトルの通りです。

ビット演算あまり使ったことがないので使ってみたかった、、だけ、、

import { writeAllSync } from "https://deno.land/std/streams/conversion.ts";
const print = (s: string) => writeAllSync(Deno.stdout, new TextEncoder().encode(s));
const denosuko = ["デノ", "スコ"]
const answer = 0b111011101110
let i = 0
while (true) {
    const c = (Math.random() > .5) ? 0 : 1
    print(denosuko[c])
    if (((answer >> i & 1) ^ c) == 0) {
        i++
        if (i == 12) break
        continue
    }
    i = 0
    console.log(" ")
}
print(" ラブ注入♡")

Hyperの壁紙を設定する2022

みなさんターミナルにHyperつかってますか? Electronベースなマルチプラットフォームなターミナリアプリです。

hyper.is

特に見た目に関するカスタマイズがやりやすく

klaussinani.github.io

のようなテーマを丸切り変えるプラグインが有名ですね。

壁紙を設定する 結論

github.com

このプラグインを利用します。 基本的にreadmeに書いてあることを行えば問題ありません。

本文

今回はhyperに壁紙を設定する方法が日本語で見つからなかったので記事化してみます。 (ちなみに著者の環境はmacです)

簡単に検索すると hyperの背景に画像を設定する方法 - Qiita が見つかります

基本的にこの記事の方法でも実現できますがCSS設定等がめんどくさいのでプラグインで実現しましょう

インストール

hyper i hyper-background-image

画像の配置

専用のフォルダを作成してそこに画像を配置します

mkdir ~/hyper-bg/
cp [画像] ~/hyper-bg/

設定

module.exports = {
  ...

  config: {

    ...

    backgroundImage: {
      folder: '~/hyper-bg',
      colorOpacity: .6,
      position: "center", // background-position default : center
      size: "contain",  // background-size default : contain
      repeat: "no-repeat",  // background-repeat default : no-repeat
    }

    ...

こうすると作成したフォルダ内の画像がランダムに背景として切り替わります やりましたね

ただしこれで作業が捗るかは微妙ですね。。 気分上げてやっていきましょう!!!

おまけ

MacでHyperを手動でインストールすると、 ToolsInstall Hyper CLI command in PATH を行ってもhyperコマンドが使えない時があります。 これの対策は

mkdir -p /usr/local/bin

等でusr/local/binのフォルダを作ってあげると動作することがあります。 (実際ちょっとハマりました) ご参考までに

ちなみにHyperはブラウザ並みにメモリを使用するのでメモリ8GBのPC等では利用をおすすめしません、、

Vite/ReactでTodoあぷり

こんにちは。

フロントエンドの変化は激しいと揶揄されてから20年(嘘) The State of JS 2021: Libraries

を見ると、vite+typescriptが現時点で良さそうということがわかります。

そこで今日はこの二つでTodoアプリを作ってみましょう

構築

モダンなjsツールはscaffoldする機能がついています

https://vitejs.dev/guide/#scaffolding-your-first-vite-projectに記載があるようにやっていきます

npm create vite@latest

コマンドを打つといくつかの質問項目が表示されますが適当に選んでいくとプロジェクトが完成します

npm run devを実行するとスキャフォルドされたアプリが立ち上がりました。

簡単ですね。

ちなみにviteコマンドは開発時に使用vite buildでdist等に成果物を出力、vite previewというコマンドは単にwebサーバをdist基準で立ち上げるという処理のようです。 previewの存在意義がいまいちわかりませんが、おそらくdistに固めたものが問題ないか確認したいというケースがそれなりにあるんでしょうね。 (普段フロントエンド開発はしないのでわからん)

静的サイトのデプロイ | Vite

ちなみに作られるプロジェクトの構成とメインの中身は以下の図の通りです。

viteだとindex.htmlがpublic以下にないのが特徴的でしょうか。 公式にこだわりの一つと記載があった気がするので気になった人は探してみてください。 アプリ自体は典型的なReactアプリだなあという感じです。

Todoアプリを作る

中身自体はmuiで適当に作ります

...

できました

まあ正直なんだかわかりませんが、 左側のタスクを右側に書いてあることをやるときの気分でやれば取り掛かるのがめんどい! ということがへるんじゃないでしょうか

www.teikoku-vol.com

まあ色もやばい気もしますがこれは好みです

何か大志を持ってこれに取り組んだ気もするんですが何をしようとしていたか忘れました。

そのうちローカルストレージ対応できたらいいかなと思ってます

以上!!

skebはまだ来ないですね、、

AWS SQS でレートリミットを実現する方法 / 予約済み同時実行数を設定する必要はないです

SQSでAWS Lambdaを起動する際、Lambdaの最大起動数≒レートリミット制御をしたいときがあると思います。

検索すると、Lambdaに「予約済み同時実行数」を設定する
という方法がよく見受けられますがあまりお勧めしません。以下の方法で実装することを推奨します。

結論

SQSでタスクを一定数のLambdaで処理したい場合は、 SQS FIFOキューを使用し、メッセージグループIDの種類をLambdaを並行起動したい数になるように設定して実現することをお勧めします。

(サンプルは記事下部に記載)

FIFOキューのグループIDについて

SQSのFIFOキューのメッセージグループIDについては以下のページに記載があります docs.aws.amazon.com

単一の FIFO キューに複数の順序付けされたメッセージグループをインターリーブするには、メッセージグループ ID 値を使用します (たとえば、複数のユーザーによるセッションデータ)。このシナリオでは、複数のコンシューマーでキューを処理できますが、各ユーザーのセッションデータは FIFO 方式で処理されます。

特定のメッセージグループ ID に属するメッセージが表示されない場合、他のコンシューマーでも同じメッセージグループ ID のメッセージを処理できません。

FIFOキューはキュー内のタスクに設定されたグループIDの数だけ同時に処理されます。

例えばグループID=AのメッセージがFIFOキューから処理されている場合、
そのタスクより後に追加されたグループAのタスクは、先頭のタスク処理が終了しない限り、コンシューマから取得できない状態になり処理されません。

すでにグループAのタスクが処理されている状態で、グループAのタスクを追加した場合、 追加されたタスクによってLamdaが新規に立ち上がることはありません。

一方で処理中でない、グループBのタスクを追加した場合には、
Lambdaが新規に立ち上がり、グループBのタスクがそれより前に追加されたグループAのタスクより先に処理されることになります。

このメッセージグループIDの仕組みを使うことで、Lambdaの並行起動数に制限がかけられます。

例えば、タスクを10件のLambdaで継続的に処理したい場合、
タスクをキューに追加するproducer側でメッセージIDに1-10の数字のどれかを振りましょう。
(なるべく各グループが均一になるように、処理時刻の小数点以下の秒数や乱数を使って発番するのが良いです)

こうすれば、タスクが最大10個のLambdaに割ふられ、想定した起動数以下でタスクを処理がされるようになります。

なぜLambdaの予約済み実行数を使わないのか?

SQSのレートリミットの実現方法を検索すると、 以下のドキュメントに記載されたLambdaの予約済み実行数の設定を使用する方法を見つけられます。

この方法は個人的にはあまりおすすめしません。

docs.aws.amazon.com

予約済み実行数を使わない理由/おすすめしない理由としては以下の2点があります

①SQSから起動するLambdaに予約済み実行数を設定すると、SQSタスクが大量に失敗する
②予約済み同時実行数はAWSアカウント単位の同時実行数を先食いする

①SQSから起動するLambdaに予約済み実行数を設定すると、SQSタスクが大量に失敗する

予約済み実行数を使わない方が良い一番の理由がこの問題です。 予約済み実行数(以下予約数)を使用した場合、Lambdaの処理/SQSの設定をタスクが大量に失敗する前提の設定にしないと意図通り動作しないためです。

以下の記事でも似たような問題提起がされています。
(この記事を書いた一番のモチベーションが、以下の記事と似た記述が日本語記事で見つからなかっためです)

We love AWS Lambda, but its concurrency handling with SQS is silly.

even if your concurrency is 2, Lambda will still start out by pulling 5 batches from SQS at a time, and it will still scale up to 1000.

So your Lambda is only allowed to run 2 at a time, but SQS is trying to push hundreds of events to Lambda, then almost all those events are going to be bounced back to SQS And this is a problem because (typically) you’ll configure SQS to only attempt each item a few times before it gives up and sends that item to the Dead Letter Queue (DLQ).

これが意味することは、

  • 仮に予約済み実行数をLambdaに設定しても、SQSはLambdaを立ち上げようとする
  • ただしLambda側に予約数が設定されているため、このLambdaの立ち上げは失敗する
    • タスクはLambdaでエラーが起きた場合と同じように、非表示状態のまま、タスクの失敗数が1加算される
  • このときSQSの再実行ポリシーの最大受信数をが少ない設定だった場合、タスクが数回失敗扱いになるだけでそのタスクがDLQに送られ処理されなくなる

というようなところです。

具体例

具体的な数字を使った例で説明すると、

Lambdaの予約数を使ってSQSのLambda並行数を制御した場合で、 タスクが10000個、予約数を5としたとき

  • 最初に先頭タスク5個が処理されLambdaが5個起動する、残りの9995件のタスクは失敗扱いになり非表示になる
  • SQSの可視性タイムアウト秒数後、次に5-10番目のタスクが処理される。残りの9990件のタスクは失敗扱いになり非表示なる
  • 繰り返す…

となり、簡単に見積もると10000番目ののタスクは1999回失敗することになります。 しかも可視性タイムアウト秒数が30秒だったとすると、実行されるのは1000分後になります。

もしSQSにこの設定をした場合、必ず失敗すタスクをキューに追加してしまった場合でも、そのタスクがDLQに移動するのは1000分後になるため、 1000分後までそのキューは無駄なリトライを繰り返すことになってしまいます。

(実際にはバッチ数やタスク処理の具合によってこの数字にはならないかも、ただ同様の現象は起きる)

このようにLambdaの予約済み実行数による並行制御は特にタスクが大量に入るようなときには非常に扱いが難しくなります。 以上が予約済み実行数をおすすめしない理由の1つになります。

②予約済み同時実行数はAWSアカウント単位の同時実行数を先食いする

予約数をお奨めしない理由の2つ目が、Lambda全体の同時実行数を事前に消費してしまうという点です。

みなさんがLambdaを利用するのは、「自動的に起動数が拡大するためほぼ無制限にスケールアウトする」というのが1つの理由だと思いますが、 このLambdaがどこまでスケールアウトするか(=起動数が増やせるか)は、アカウント内のLambda最大同時実行数としてクオータで規定されています(デフォルトだと1000)。

Lambda クォータ - AWS Lambda

この同時実行数はアカウント内で共有されるリソースとして考えることができます。 一方で、予約済み実行数をある特定のLambdaに設定した場合、この共有リソースである同時実行数をそのLambdaで占有することになります。 予約した実行数を単にそのLambdaの最大並列数を制限する機能と考えると踏んでしまいそうな挙動になりますが、 実際には予約済み同時実行数は他のLambdaの起動にも影響を及ぼす設定です。

例えばあるLambda:Aに予約数200を設定した場合、残りのLambdaは最大でも800までしかスケールアウトできなくなります (アカウントのLambda起動数がデフォルト1000の場合)。 もし月に1回しか動作しないバッチ用途のためにLambdaに予約数を設定してしまうと、 バッチが動作しないほとんどの時間帯のスケールアウト上限を犠牲にしてしまうことになってしまうわけですね。

この挙動は以下のドキュメントにも記載されています。 docs.aws.amazon.com

予約同時実行 – 予約同時実行は、関数の同時インスタンスの最大数を保証します。ある関数が予約済同時実行数を使用している場合、他の関数はその同時実行数を使用できません。関数に対して予約済み同時実行を設定する場合には料金はかかりません。

予約済み同時実行数は、他のLambda実行に影響を及ぼす場合があるため、使う場合はよく注意して使用した方が良いですね。
特にキューを500個のLambdaで処理するために、予約済み同時実行数に大きい数を設定すると、他のLambdaがスケールアウトする余地がなくなってしまうことが予想されます。 このためFIFOキューを使って実装した方が他のLambda実行にも影響を及ばさず良いかと思います。

おまけ-実際に起動数を制御してみた

実際にFIFOキューでLambdaの同時実行数が制御できることを確認してみました。

Lambdaの処理

const sleep = msec => new Promise(resolve => setTimeout(resolve, msec));

exports.handler = async (event) => {
    
    console.log("###START",event.Records[0].body)
    
    await sleep(3000)
    
    console.log("###END",event.Records[0].body)
};

この際設定でSQS処理時のバッチサイズを1にしています。 またSQSの種別はFIFOキューです。

タスクの追加

グループIDを設定したタスクを追加します。

aws sqs send-message --queue-url "$MYSQS" --message-body "GroupA:Task1" --message-group-id "A"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupA:Task2" --message-group-id "A" 
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupA:Task3" --message-group-id "A" 
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupB:Task1" --message-group-id "B"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupB:Task2" --message-group-id "B"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupB:Task3" --message-group-id "B"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupC:Task1" --message-group-id "C"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupC:Task2" --message-group-id "C"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupC:Task3" --message-group-id "C"
aws sqs send-message --queue-url "$MYSQS" --message-body "GroupD:Task1" --message-group-id "D"

実行結果

キューのタスクが一定数のLambdaで処理されている様子を確認できました。

赤、青、緑、黄色がそれぞれグループABCDの処理を表しています。 これを見ると想定通り、グループの数だけlambda処理が並行起動していることが確認できます。

補足

もしタスクがあまり多くない場合は予約済み実行数+SQSでも数回のリトライ内でタスクを処理し切れるため、 状況に応じた実現方法を選択するのが良いかと思います。

ただし、その場合でも初めからFIFOキュー+グループIDを設定する方が、 無駄なLambda失敗数がメトリクスに上がることがないため良いかと思います。

注意事項

FIFOキューを使った実現方法にも一部制限があります。

Amazon SQS 可視性タイムアウト - Amazon Simple Queue Service

FIFO キューの場合、最大 20,000 のインフライトメッセージが存在できます FIFO キューは、最初の 20k メッセージを検索して、使用可能なメッセージグループを判別します。つまり、単一のメッセージグループにメッセージのバックログがある場合、バックログからメッセージを正常に消費するまで、後からキューに送信された他のメッセージグループからのメッセージを使用することはできません。

FIFOキューがタスクのグループIDを認識して、現在実行中でないグループのタスクを処理できる状態にするためには、 そのメッセージが先頭から20k以内である必要があります。 タスクの量が20kを超える場合は、タスクを追加する順番によっては意図した数のLambda起動数より少なくなる恐れがあります。

また、そもそもFIFOキューは通常のキューと比べるとメッセージ重複排除IDという概念があるため、その点も注意してください。

補足2 FIFOキューで先頭のタスクがエラーになった場合後続のメッセージは処理されるのか?

FIFOキュー内のグループの先頭のタスクがエラーで可視性タイムアウトに陥った際に、後続の同じグループIDのものが処理されるのかについて、 可視性タイムアウトのページに以下の記載があります

https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html

メッセージグループ ID があるメッセージを受信した場合、そのメッセージを削除しない、または表示されない限り、同じメッセージグループ ID のメッセージはそれ以上返されません

表示されない限り=可視性タイムアウトの時間のため、 グループIDが設定されたタスクが非表示になっただけでは後続の同じグループのタスクは非表示のままになるかと思います。

以上