every Tech Blog

株式会社エブリーのTech Blogです。

プログラムがCPUに理解されるまでのプロセスをまとめてみた

プログラムがCPUに理解されるまでのプロセスをまとめてみた

目次

はじめに

こんにちは。 トモニテ開発部ソフトウェアエンジニア兼、CTO室Dev Enableグループの庄司(ktanonymous)です。
every Tech Blog Advent Calendar 2024(夏) の2日目の記事執筆担当者として参加させていただいております!

tech.every.tv

今回の記事では、普段書いているプログラムがCPUによってどのように理解されているのかについて、気になって勉強したのでまとめてみたいと思います。
(厳密には異なる表現があるかもしれませんが、概念的な理解を目指すものなので、ご容赦ください。)

CPUが理解できる言葉

CPUが理解できる言葉は、機械語と呼ばれるものです。 我々エンジニアが普段から書いているプログラミング言語は、CPUから見れば 「意味のわからない、ただの文字列でしかない」と言えるでしょう。
機械語とは、例えば、以下のように表現することができます。(CPUの種類によって表現が異なっていたり、そもそもの解読が辛かったりするので正確な表現・値ではありません)

01 00 10

各数値は16進数で表されており、これで「アドレス00番地に値10を書き込む」というように、処理(01)と必要な対象を組み合わせて1つの命令を表現します。 では、プログラミング言語はどのように機械語としてCPUに解釈されるのでしょうか。

プログラミング言語が機械語として理解されるまで

アセンブリ言語

現在一般的に使われているプログラミング言語の話をする前に、アセンブリ言語について触れたいと思います。
アセンブリ言語とは、人間が理解しやすいように機械語と1対1で対応させた言語です。 アセンブリ言語がアセンブラと呼ばれるプログラムによって機械語に変換されることで、CPUが言語を理解できるようになります。 アセンブリ言語に関しては、実際にシェル上でobjdumpコマンド1を利用することで確認することができます(これはオブジェクトファイルの情報を表示するコマンドですが、-dフラグをつけることで機械語を逆アセンブルすることができます)。 例として、筆者のマシン上でecho命令を逆アセンブルしてみます。

$ objdump -d /bin/echo

すると、以下のような出力が得られます。 (なお、出力結果の全体は非常に長いので、先頭の数行を抜粋しています)

/bin/echo (architecture x86_64):
(__TEXT,__text) section
100000bbc:      55      pushq   %rbp
100000bbd:      48 89 e5        movq    %rsp, %rbp
100000bc0:      41 57   pushq   %r15
100000bc2:      41 56   pushq   %r14
100000bc4:      41 55   pushq   %r13
100000bc6:      41 54   pushq   %r12
100000bc8:      53      pushq   %rbx
100000bc9:      48 83 ec 28     subq    $40, %rsp
...

左から、ファイル(今回は /bin/echo )上でのオフセット、実際の機械語(16進数の値2~4つの組)、機械語に対応する命令を表すニーモニック(mnemonic)の順に並んでいます。

プログラミング言語の解釈

一般的に、我々が日々書いているソースコードは、アセンブリ言語への変換を目指して解釈が進められ、最終的にCPUが実行可能な機械語へと変換されます。
この解釈の過程を担っているのが、コンパイラやインタプリタと呼ばれるものになります。 コンパイル型言語やインタプリタ型言語というのは、この解釈の過程がどのように行われるかによって分類されます。

コンパイラ

コンパイラ  C は以下のように定義できます2 (説明のため一部表現を変えています)。

言語  L_2 のプログラムを言語  L_1 のプログラムに変換するプログラム  C

一般的なコンパイル型言語では、ソースコードをアセンブリ言語まで変換する役割をコンパイラが担っていることが多いでしょう(アセンブラはコンパイラに含まれている場合もあります)。 この定義から考えると、アセンブラもコンパイラの一種と言えると思いますが、 アセンブリ言語から機械語への変換は、変換元がアセンブリ言語であることを強調するためにアセンブラと呼ばれることがあります。
コンパイル型言語の1つとして、弊社でも利用されているGo言語が挙げられます。 Go言語では以下のステップを経てソースコードがコンパイルされます3

  1. Parsing
    1. Lexical analysis (tokenize)
    2. Syntax analysis (parse)
    3. AST construction
  2. Type checking
  3. IR construction
  4. Middle end (最適化)
  5. Walk (順序評価、構文の低級化)
  6. Generic SSA(Static Single Assignment4)
  7. Generating machine code

Goのコンパイラにはアセンブラも含まれているため、最終的には機械語に変換されていることがわかります。

Goの標準のコンパイラである gc はGoで実装されています。 これは、セルフホスティングと呼ばれる手法で、自身の言語で自身のコンパイラを書くというものです。 gcは元々C言語で書かれていましたが、この手法を用いることでGoで書かれたコンパイラが実現されています5。 自身の言語で実装されたコンパイラを実行するために、コンパイラのコード自身がコンパイルされている必要があります。 そのため、異なる言語で実装されたコンパイラ  C_1 を用いて自身の言語で実装されたコンパイラ  C_2 をコンパイルし、 最後に  C_2  C_2 自身をコンパイルすることで、自身の言語で書かれたコンパイラが完成します(イメージ)。 また、コンパイラの実現方法の他に、コンパイルの手法にもJIT(Just-In-Time)コンパイラ6やAOT(Ahead-Of-Time)コンパイラ7などの種類があります。 TypeScriptからJavaScriptへの変換(トランスパイル)もコンパイルの一種です。

リンカ

一般的に、プログラムは複数のソースコードから構成されます。そのため、 コンパイラによって生成されたそれぞれのオブジェクトファイルは、そのままでは実行可能な1つのプログラムとはなりません。 これらのオブジェクトファイルをリンカと呼ばれるプログラムによって結合することで、実行可能な1つのプログラムが生成されます。
リンカは、関数のエントリーポイント情報を補完するなどして、複数のオブジェクトファイルを結合した1つの実行可能ファイルなどを生成します。 通常、ソースコードを機械語にコンパイルしてリンクするまでの過程を指して「ビルド」と呼びます。 なお、生成された実行可能ファイルは、ローダーによってストレージ(外部記憶装置)からメインメモリ(RAM)などに読み込まれます。
また、リンクには静的リンクと動的リンクの2種類があります。 静的リンクは、プログラムの実行に必要なライブラリなどを単一の実行ファイル内部にリンクする方法、 動的リンクは、呼び出される側のライブラリが実行時にリンクされる方法です。 リンク方法が静的か動的かどうかで実行ファイルのサイズや開発サイクルのスピードなどに違いが出てきます。

インタープリタ

インタープリタ  I は以下のように定義できます8 (説明のため一部表現を変えています)。

言語  L_1 を用いて実現した、言語  L_2 のプログラムが動作するプログラム  I

インタープリタは、実行時にソースコードを逐次解釈して実行するものです。 インタープリタの解釈手法には、ソースコードをそのまま逐次解釈するものもあれば、 一度バイトコードなどに変換(コンパイル)してから逐次解釈するものもあります。
例えば、Pythonの標準的なインタープリタであるCPython9は、 ソースコードをバイトコード(中間表現)に変換してから逐次解釈され、 PVM(Python Virtual Machine)によって処理されます10。 ちなみに、Pythonのバイトコードはdisモジュール11を利用することで確認することもできます。 例えば、以下のようなコードを実行する場合を考えます(Google Colaboratoryでの実行を前提にしています)。

import dis

def print_hello():
  print("Hello!")

dis.dis(print_hello)

このコードを実行すると、以下のような出力が得られます。

  4           0 LOAD_GLOBAL              0 (print)
              2 LOAD_CONST               1 ('Hello!')
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

左から、ソースファイル内での行番号、命令のバイトコード(とその該当バイトインデックス)、 命令が取得する引数の参照インデックスと引数の順に並んでいます。

まとめ

今回の記事では、普段意識することのなかったプログラムの処理系について勉強したことをアウトプットしてみました。
これを知ったからといって普段のコーディングが劇的に変わるということはないと思いますが、 こういった基礎的な知識がシビアなシーンでは役に立つことも多いと思います。 この記事が、「なんかそれっぽいこと書いてるだけで勝手にPCが結果を出してくれる」を脱却したい人の一助になれば幸いです。
最後まで読んでいただき、ありがとうございました。

参考

Vuex から Pinia への移行を行いました

はじめに

この記事は、every Tech Blog Advent Calendar 2024(夏) の1日目の記事です。

DELISH KITCHEN開発部の羽馬(@NaokiHaba)です。

この記事では、DELISH KITCHEN チラシ で使用している Vuex の Pinia への移行について紹介します。

chirashi.delishkitchen.tv

本記事では、これらの知識があることを前提に説明を進めます。

  • Vue.jsの基本的な知識
  • Nuxt.jsの基本的な知識
  • Vuexの基本的な知識

Piniaとは

Pinia(ピーニャ)は、Vue.js用の新しい状態管理ライブラリです。Vuexの次のイテレーションとして開発が始まり、Vuex 5に組み込むことを想定していたアイデアを多く取り入れています。

pinia.vuejs.org

Piniaは、Vuexと比較して以下のような特徴や利点があります。

  • シンプルなAPIを提供し、学習コストが低い
  • TypeScriptとの連携が強化され、型の恩恵を受けやすい
  • モジュール方式を採用せず、ストアを個別に定義できるため、コードの可読性や保守性が向上する
  • Vue Devtoolsとの統合が進んでおり、開発体験が良い

Piniaは、Vue.js v2とv3の両方に対応しており、Nuxt.jsにも対応しています。Nuxt v3からは、VuexからPiniaが公式に推奨されるようになりました。

なぜPiniaに移行するのか

DELISH KITCHEN チラシ では、以下の理由からPiniaへの移行を決定しました。

  1. Nuxt3への移行を見据えて、早めにPiniaを導入しておきたかった
  2. Vuex は現在メンテナンスモードであり、今後のアップデートが見込めないため
  3. Nuxt3以降もPiniaの公式サポートが続くと予想されるため

Piniaへの移行によって、Nuxt3への移行をスムーズに進めることができると考えました。

移行の手順

1. Piniaの導入

まずは、Pinia を導入します。

pinia.vuejs.org

$ yarn add pinia @pinia/nuxt
# or with npm
$ npm install pinia @pinia/nuxt

次に、nuxt.config.js に Pinia の設定を追加します。 移行時点では、Vuex と Pinia を併用することとなるため、disableVuex を false に設定します (disableVuex はデフォルトで true になっているため、Vuex が無効化されます)

// nuxt.config.js
export default defineNuxtConfig({
  buildModules: [
    // set `disableVuex` to false if you need to use Vuex alongside Pinia
    [ '@pinia/nuxt', { disableVuex: false } ],
  ],
})

以上で、Pinia の導入は完了です。

2. VuexストアのPiniaストアへの移行

次に、既存のVuexストアをPiniaストアに移行します。 Piniaでは、ストアをdefineStore関数を使って定義します。defineStore関数には、ストアの名前を表すidと、ストアの定義を表すoptionsの2つの引数を渡します。

pinia.vuejs.org

以下は、Vuexストアの例です。

// store/todo.js
export default {
  state: {
    todos: [],
  },
  mutations: {
    setTodos(state, todos) {
      state.todos = todos
    },
  },
  actions: {
    async fetchTodo({ commit }, id) {
      try {
        const response = await this.$axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
        commit('setTodos', [ response.data ])
      } catch (error) {
        console.error(error)
      }
    },
  },
  getters: {
    allTodos: state => state.todos,
  },
}

このVuexストアを、Piniaストアに移行すると以下のようになります。

// stores/todo.js
import { defineStore } from 'pinia'

export const useTodoStore = defineStore('todos', {
  state: () => ({
    todos: [],
  }),
  actions: {
    async fetchTodo(id) {
      try {
        const response = await this.$nuxtAxios.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
        this.todos = [ response.data ]
      } catch (error) {
        console.error(error)
      }
    },
  },
  getters: {
    allTodos: (state) => state.todos,
  },
})

Piniaストアでは、mutationsが削除され、actionsとgettersのみが残っています。これは、Piniaではmutationsの概念がなくなり、actionsで直接ステートを更新するためです。

また、actions内でのthisの扱いが変わっています。Piniaでは、thisがストアのインスタンスを指すため、this.todosのように直接ステートを更新できます。

ここで、this.$axiosthis.$nuxtAxios に変更されていることに注目してください。

Piniaでは、ストアの中で this がストアのインスタンスを指します。したがって、Vuexストアで使っていた this.$axios をそのまま使うことはできません。

代わりに、Nuxtのコンテキストからプラグインを介して $axios を取得し、this.$nuxtAxios として使用しています。

このプラグインは、以下のように定義します。

// plugins/pinia-inject-axios.js
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.$pinia.use(() => ({
    $nuxtAxios: markRaw(nuxtApp.$axios),
  }));
});

そして、nuxt.config.js でこのプラグインを登録します。

Nuxt3では、$fetch を使うことが推奨されており、@nuxtjs/axios は利用できないため、このプラグインは不要になります。

// nuxt.config.js
export default defineNuxtConfig({
  plugins: [
    '~/plugins/pinia-inject-axios.js',
  ],
})

nuxtServerInit の扱い

Vuexでは、nuxtServerInit はサーバーサイドレンダリング(SSR)時に、サーバー側での初期化処理を行うための特別なアクションでした。Nuxt.jsでは、SSR時にstore ディレクトリ内の各ストアのnuxtServerInitアクションが自動で呼び出される仕組みがあります。

一方、PiniaではnuxtServerInitが自動で呼び出される仕組みがありません。代わりに、pluginsmiddleware を利用して、nuxtServerInitの処理を移行する必要があります。

例えば、plugins/nuxt-server-init.jsというファイルを作成し、以下のようなコードを記述します。

export default defineNuxtPlugin(nuxtApp => {
  if (process.server) {
    // サーバー側での初期化処理をここに記述
  }
})

3. コンポーネント内でのストアの利用方法の変更

最後に、コンポーネント内でのストアの利用方法を変更します。 VuexではmapState、mapGetters、mapActionsなどのヘルパー関数を使ってストアにアクセスしていました。 Piniaでも同様のヘルパー関数が用意されていますが、mapGettersの代わりにmapStateを使うことが推奨されています。

pinia.vuejs.org

<template>
  <div>
    <div v-for="todo in todos" :key="todo.id">
      {{ todo.title }}
    </div>
    <button @click="fetchTodo(1)">Fetch Todo</button>
  </div>
</template>

<script>
  import { mapState, mapActions } from 'pinia'
  import { useTodosStore } from '~/stores/todosStore'

  export default {
    fetch({ app, error, $pinia }) {
      const todosStore = useTodosStore($pinia)
      todosStore.fetchTodo(1)
    },
    computed: {
      ...mapState(useTodosStore, [ 'todos' ]),
    },
    methods: {
      ...mapActions(useTodosStore, [ 'fetchTodo' ]),
    },
  }
</script>

Composition APIを使う場合は、useStore関数を使ってストアのインスタンスを取得し、直接ストアの状態やアクションにアクセスできます。

<script setup>
  import { useTodosStore } from '~/stores/todosStore'

  const todosStore = useTodosStore()

  await todosStore.fetchTodo(1)
</script>

<template>
  <div>
    <div v-for="todo in todosStore.todos" :key="todo.id">
      {{ todo.title }}
    </div>
    <button @click="todosStore.fetchTodo(1)">Fetch Todo</button>
  </div>
</template>

まとめ

この記事では、DELISH KITCHEN チラシ におけるVuexからPiniaへの移行について紹介しました。 Piniaは、Vuexと比べてシンプルなAPIを提供し、TypeScriptとの連携が強化されているため、Nuxt3での開発をスムーズに進めることができます。 Nuxt3での開発を行う際には、ぜひPiniaの導入を検討してみてください。

every Tech Blog Advent Calendar 2024(夏)開催のお知らせ

はじめに

DELISH KITCHEN開発部 兼 Dev Enableチームの羽馬(@NaokiHaba)です。

初夏の陽気が心地よい今日この頃、every Tech Blog ではもうすでに夏へのカウントダウンが始まっています。

そして今年は、その夏を少し先取りする形で、6月にアドベントカレンダーを開催します!

every Tech Blog Advent Calendar とは

every Tech Blog Advent Calendar は、2023年12月に始まった弊社のエンジニアによる技術ブログ企画です。

Advent Calendarにちなんで、12月は1日から25日まで日替わりで記事を公開してきました。 Webフロントエンドからバックエンド、インフラ、機械学習、データ分析など幅広い分野の記事が集まり、多くの方にご覧いただけました。

tech.every.tv

every Tech Blog Advent Calendar 2024 (夏) の見どころ

今回のアドベントカレンダーでは、前回の知見を踏まえつつ、さらに多様で深い技術記事を 6月1日から28日までの28日間 毎日お届けします!

暑い夏を熱いテクノロジーで乗り切るべく、エンジニアたちが知恵を絞って記事を執筆中です。

ご期待ください!

注目の企画として、今回はエブリーがスポンサーを務める2つのカンファレンスを盛り上げるべく、カウントダウン企画を実施します!

6/3〜6/7 Go Conference 2024カウントダウンブログ

Go Conference 2024が開催される6月8日まで、Go言語に関する記事を毎日公開します。 イベント詳細はこちらから!

gocon.jp

6/19〜6/21 KotlinFest 2024 カウントダウンブログ

KotlinFest 2024が開催される6月22日まで、Kotlinに関する記事を毎日公開します。

イベント詳細はこちらから!

www.kotlinfest.dev

公開日 テーマ URL
2024/06/01 Vuex から Pinia への移行を行いました https://tech.every.tv/entry/2024/06/01/170000
2024/06/02 プログラムが CPU に理解されるまでのプロセスをまとめてみた https://tech.every.tv/entry/2024/06/02/103000
2024/06/03 go 言語で cobra と slog を使った CLI ツール開発 https://tech.every.tv/entry/2024/06/03/103933
2024/06/04 Go 言語の並行処理: ゴルーチンとチャネルの活用法について https://tech.every.tv/entry/2024/06/04/100307
2024/06/05 ネットスーパーリプレイス〜長大なクエリと向きあう編〜 https://tech.every.tv/entry/2024/06/05/150124
2024/06/06 Go 言語で行うメール解析 https://tech.every.tv/entry/2024/06/06/192547
2024/06/07 DELISH KITCHEN のユニットテストで使用しているライブラリ https://tech.every.tv/entry/2024/06/07/104820
2024/06/08 Go Conference 2024 に プラチナ Go ルドスポンサー として参加しました! https://tech.every.tv/entry/2024/06/08/200152
2024/06/09 レシピ動画からサムネイル画像を自動抽出する AI システムを作りました https://tech.every.tv/entry/2024/06/09
2024/06/10 社内ナレッジ活用のための RAG 基盤の PoC を行いました https://tech.every.tv/entry/2024/06/10/110918
2024/06/11 API Gateway から Amazon Data Firehose へ Lambda を使わずにデータを流す https://tech.every.tv/entry/20240611
2024/06/12 Xcode 15 の画像/色のシンボル自動生成機能を SPM マルチモジュール環境で使う https://tech.every.tv/entry/2024/06/12/111801
2024/06/13 Databricks Model Serving と AWS API Gateway で作る ML API https://tech.every.tv/entry/2024/06/13/170411
2024/06/14 mamadays.tv から tomonite.com へドメインを変更しました https://tech.every.tv/entry/2024/06/14/144222
2024/06/15 新規プロダクトのリポジトリ構成にモノレポを採用してみた https://tech.every.tv/entry/2024/06/15/0001
2024/06/16 N1 分析してみる https://tech.every.tv/entry/2024/06/16/000000
2024/06/17 ML のスモールスタート時に Databricks の Feature Store を導入するべきか否か https://tech.every.tv/entry/2024/06/17/140157
2024/06/18 Flutter エンジニアが年 150 万円のサーバー費用を削減する話 https://tech.every.tv/entry/2024/06/18/175820
2024/06/19 LiveData を Kotlin Coroutines Flow に移行した話 https://tech.every.tv/entry/2024/06/19/114100
2024/06/20 Android プロジェクトの KSP 化を検討するにあたって https://tech.every.tv/entry/2024/06/20/095753
2024/06/21 8 年前に Kotlin を採用してたくさん恩恵を受けた話 https://tech.every.tv/entry/2024/06/21/100756
2024/06/22 Kotlin Fest 2024 に ひよこスポンサー として参加してきました! https://tech.every.tv/entry/2024/06/22/185445
2024/06/23 AWS Summit Japan 2024 に参加しました https://tech.every.tv/entry/2024/06/23/144931
2024/06/24 RDS で EBS BurstBalance が枯渇した事例の紹介 https://tech.every.tv/entry/2024/06/24/195434
2024/06/25 Go 言語で multipart/form-data を使用して画像を受け取り外部に送信する https://tech.every.tv/entry/2024/06/25/110115
2024/06/26 Amazon QuickSight を使用してインタラクティブな可視化をしてみる https://tech.every.tv/entry/2024/06/26/114130
2024/06/27 リアーキテクチャを支えるテスト駆動開発:効果的なリファクタリングの方法 https://tech.every.tv/entry/2024/06/27/121736
2024/06/28 Golangでアプリ課金(iab/iap)を実装するときは awa/go-iap が便利って話 https://tech.every.tv/entry/2024/06/28/115518

投稿された記事は、株式会社エブリー 開発部の公式X でポストするとともに、こちらの技術ブログにも順次掲載していきますので、ぜひブックマークやコメント・シェアをお願いします!

それでは、6月1日からお楽しみに!

最後に

エブリーでは、ともに働く仲間を募集しています。

テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください!

corp.every.tv

最後までお読みいただき、ありがとうございました!

TSKaigi 2024 に参加してきました!

TSKaigi 2024 に参加してきました!

はじめに

Dev Enableチームの羽馬( NaokiHaba) と 庄司(ktanonymous )です。

2024年5月11日(水)に開催されたTSKaigi 2024に参加してきましたので、イベントの様子や印象に残ったセッションをいくつかご紹介します。

各セッションのアーカイブも公開予定とのことですので、ぜひ公式サイト・YouTubeチャンネルなどをチェックしてみてください。

tskaigi.org

www.youtube.com

イベントの様子

TSKaigi 2024は、今年から開催された新しいイベントです。TypeScriptを中心にしたカンファレンスで、TypeScriptの最新情報や活用事例などが紹介されました。

会場には、国内外から多くのエンジニアが集まり、盛況のうちに開催されました。会場内では、様々なブースが設けられ、最新のツールやサービスの紹介が行われていました。

特に印象に残ったブースは、アセンド株式会社 様のブースで開催された 「TypeScriptコンパイルチャレンジ」です。

このコンパイルチャレンジは、以下のような内容でした。

  1. 参加者は、青色の「型カード」を引く
  2. 机に並べられた赤色の「値カード」から一つをめくる
  3. 引いた型カードと値カードを使って、TypeScriptのコンパイルにチャレンジ
  4. コンパイルに成功すると、景品が贈呈される(高難易度コンパイルに成功した方には、HHKBなどの豪華景品も)

弊社メンバーも、あと一歩のところで当選を逃してしまいましたが、楽しい体験ができました。

参加レポート

Keynote: What's New in TypeScript

発表者: Daniel Rosenwasser さん(https://twitter.com/drosenwasser) レポート: 庄司

Microsoft / TypeScript Principal Product Manager の Daniel Rosenwasser さんによる Keynote では、TypeScript の最新情報が紹介されました。

TypeScript 5.4 および TypeScript 5.5 Beta の新機能について、 ライブコーディングを交えながら、各機能の使い方や利点が丁寧に解説されていました。

主な新機能は以下の通りです:

  • TypeScript 5.4
    • The NoInfer Utility Type
    • Preserved Narrowing in Closures Following Last Assignments
  • TypeScript 5.5 Beta
    • Type Imports in JSDoc
    • Regular Expression Syntax Checking
    • Inferred Type Predicates
    • Isolated Declarations

特に NoInfer は型推論を制御する上で強力な機能だと感じました。 型の絞り込み (Narrowing) の改善や JSDoc での型インポートのサポートなど、日々の開発で嬉しい機能が多数含まれていました。 Regular Expression の構文チェックは地味ながら実用的な機能追加だと思います。 TypeScript は着実に進化を続けており、次のバージョンが今から楽しみです。

TypeScript の抽象構文木を用いた、数百を超える API の大規模リファクタリング戦略

発表者: やなえもん さん(https://twitter.com/yanaemon169) レポート: 庄司

speakerdeck.com

こちらのセッションでは、数百のAPIを抱える Express コードを、AST(抽象構文木, Abstract Syntax Tree) を利用して Nest.js コードに大規模移行するという取り組みが紹介されました。

コードのリプレイスと言えば、正規表現を利用したスクリプトによる変換やIDEによる一括置換などが一般的ですが、こちらは AST を利用しているという点が新鮮でした。

AST を利用することで、微妙な表記揺れなどを気にせず、コードの構造に則したリプレイスが可能になるという話には説得力がありました。

TypeScriptのコンパイラでもASTが利用されているように、ASTとTypeScriptの相性の良さを感じました。また、近年の生成AI技術の発展によって、ASTの取り扱いもより容易になるのではないかと期待が持てます。

一方で、レビュアーの負担が大きいという課題にも触れられていましたが、全体としてとてもチャレンジングで興味深い取り組みだと感じました。

AST については、HireRoo さん(https://twitter.com/hirerooinc) が発表された TypeScript ASTを利用したコードジェネレーターの実装入門 でも詳しく解説されていましたので、興味のある方はそちらもチェックしてみてください。

TypeScriptから始めるVR生活

発表者: TamaG さん(https://twitter.com/TAMAGOKAKE_G_) レポート:羽馬

speakerdeck.com

Resonite上でビジュアルプログラミング言語「ProtoFlux」を使った開発の様子が紹介されました。

ProtoFluxは「ノード」と「ノード」をつなぐことでプログラミングができる言語ですが、バージョン管理や関数化ができないという問題点がありました。

これらの問題を解決すべく生まれたのが「MirageX」です。MirageXは、TypeScriptとReactを使ってResoniteの開発ができるフレームワークです。

コードベースの開発になるため、バージョン管理やAIの力を借りることができるようになりました。また、ライブラリを使うこともできるため、本格的なシューティングゲームなども作成可能です。

VRプラットフォームでのTypeScriptを使った開発事例が紹介され、その可能性と課題について理解を深めることができました。VRという新しい領域でのTypeScriptの活用法について学べる貴重な機会でした。

興味を持った方は、ぜひ MirageXのGitHubリポジトリをチェックしてみてください!

サービス開発におけるVue3とTypeScriptの親和性について

発表者: からころ / karacoro さん(https://twitter.com/karan_corons) レポート:羽馬

speakerdeck.com

Vue3ではComposition APIの登場により、コンポーネントのロジックを外部ファイルに切り出しやすくなり、型付けも改善されました。

また、コンポーネントランタイムの型付け強化により、Props、Emit、Provide/Injectなどでも型の恩恵を受けられるようになっています。

さらに、Volar.jsとvuejs/language-toolsの貢献により、テンプレートへの型の反映などエディタ連携の問題も解決されました。

講演の丁寧な解説と豊富なコード例は、Vue3とTypeScriptを活用したサービス開発のベストプラクティスを学ぶ上で非常に参考になる内容だと感じました。

まとめ

TSKaigi 2024は、TypeScriptを中心にしたカンファレンスとして、多くのエンジニアにとって有益な情報が得られるイベントでした。

TypeScriptの最新情報や活用事例を学ぶことができ、新しい技術やアイデアに触れることができました。

今後も、TypeScriptコミュニティの発展と、エンジニアのスキルアップに貢献するイベントとして、TSKaigiが続けられていくことを期待しています。

また、今回の参加レポートが、TypeScriptを学びたい方や、TypeScriptを活用したい方の参考になれば幸いです。

最後に

エブリーでは、ともに働く仲間を募集しています。

テックブログを読んで少しでもエブリーに興味を持っていただけた方は、ぜひ一度カジュアル面談にお越しください!

corp.every.tv

最後までお読みいただき、ありがとうございました!

fluentd/fluent-bitでTreasure Dataにログを送信するときにハマった話

はじめに

DelishKitchenヘルシカでインフラをやったりバックエンドをやったりしているyoshikenです。

今回は、Treasure Dataにログを送信しようとfluentdとfluent-bitを使っていたときにハマった話を書きます。

fluentdからfluent-bitへ

もともと弊社では歴史的背景でfluentdを使っていました。が、

  1. 大々的なlogの加工が必要なものはTreasure Dataなど別サービスで行う
  2. リソースの消費がやや気になる
  3. FireLensはじめ、Fargateでfluentbitのほうが相性が良い

などの理由より、新規サービスはすべてfluent-bitを使用することになりました。

移行から数ヶ月~数年経っていますが、flunetdに比べ軽量であるため期待した通りのパフォーマンスを発揮してくれています。

fluent-bitでTreasure Dataにログを送信できない現象

ヘルシカではアクセスログをTreasure Dataに送信する要件がありましたので、fluent-bitでTreasure Dataにログを送信する設定を行いました。

confを公式ドキュメント通りに記述しましたが、ログが送信されず、logを漁ってみると以下のようなエラーが出ていました。

[202x/xx/xx xx:xx:xx] [ warn] [output:td:td.0] HTTP status 404
{"status_code":404,"message":"Resource not found","severity":"error","error":"Resource not found","text":"Resource not found"}

同じような設定でflunetdを動かしてみると問題なく送信/挿入できたので、fluentbit固有の問題と考えdebugしていきます。

fluent-bitとfluentdではTreasure Dataプラグインの挙動が違う件

結論からいうと、fluent-bitのTreasure Dataプラグインはfluentdの同名のプラグインと挙動が微妙に異なります。

fluentdではテーブルが存在しない場合、正確に記すと「upload時に 404 not found httpステータスコードが帰ってきた場合」はテーブルを作成する処理を行います

https://github.com/treasure-data/fluent-plugin-td/blob/master/lib/fluent/plugin/out_tdlog.rb#L209-L224

      begin
        begin
          @client.import(database, table, UPLOAD_EXT, io, size, unique_str)
        rescue TreasureData::NotFoundError
          unless @auto_create_table
            raise
          end
          ensure_database_and_table(database, table)
          io.pos = 0
          retry
        end

対してfluent-bitでは、テーブルが存在しない場合でも特に追加処理などせずにそのままエラーを返却する形になっています

https://github.com/fluent/fluent-bit/blob/master/plugins/out_td/td.c#L188-L207

/* Validate HTTP status */
    if (ret == 0) {
        /* We expect a HTTP 200 OK */
        if (c->resp.status != 200) {
            if (c->resp.payload_size > 0) {
                flb_plg_warn(ctx->ins, "HTTP status %i\n%s",
                             c->resp.status, c->resp.payload);
            }
            else {
                flb_plg_warn(ctx->ins, "HTTP status %i", c->resp.status);
            }
            goto retry;
        }
        else {
            flb_plg_info(ctx->ins, "HTTP status 200 OK");
        }
    }
    else {
        flb_plg_error(ctx->ins, "http_do=%i", ret);
        goto retry;
    }

理由ついてはissueなどを漁ってみましたが、特に言及はなかったです。 一応歴史的にはfluentdも昔はflunet-bit同様にエラーをそのままエラーで返していたましたが、途中でリトライ処理が追加された形になります。

Prevent retrying unretriable errors by cyberdelia · Pull Request #35 · treasure-data/fluent-plugin-td

まとめ

まとめると以下の表になります。

fluentd fluent-bit
テーブルが存在する 送信可能 送信可能
テーブルが存在しない 自動生成 404 not found

弊チームではデータチームと話し合い、"エラーが出続けるのは健全ではない"・"fluentdと同じ仕様と勘違いし、Treasure Data側のテーブル作成を忘れてしまう"などの懸念が生じ、即座のリアルタイム性が必要なログではないため、"一度S3にoutput。その後、Treasure Dataのbatch importで挿入する。"という形で対応することとなりました。

同じようなプラグインでも挙動が異なるというレアケースを引いてしまったため、後世に同じような人がハマらないように記事に残しておきます。