那須塩原 〜 東京 🚴

8/7 (日) 4号線をずっと南下して東京駅まで走った。帰りは新幹線。

走行距離 154km 獲得標高 345m 走行時間 6時間11分 ( 経過時間 8時間44分 )

四号線を使って東京まで走ったのは 2回目。下は以前の記録

hiboma.hatenadiary.jp


今回は写真を全然撮らずに走って、最後に一枚だけ。

帰りは新幹線なすのに乗って帰宅。日曜夕方 東京発の なすのはガラガラで、客席に自転車を持ち込んでも迷惑にならない。


その他 感想

日曜日だと物流の大型車が少なめでよかった。

宇都宮〜古河は快適

宇都宮〜古河間はリラックスして走りやすい。

飛ばしたい車や大型車は新四号を使うっぽいので、(旧) 四号線はのんびり流れている。路肩もそんなに狭くなくて安全。

利根川を渡って栗橋〜幸手のあたりはしんどい

狭い + 路面の状態も悪くて凸凹も多い。しんどい!

速度

軽〜い向かい風がずっと吹いていて、風の恩恵は受けられなかったなー Garmin Edge だと 平均 25.2km 時で記録されていて Strava だと 平均 24.9km/時 の記録だった

涼しい時だと もう少し速く走れんかな

slack: metadata で構造化されたデータを扱う - bot 間の連携に使う実装例

slack で bot を動かしていて 「 別の bot のメッセージに反応して何かする bot を書くユースケースがあります *1

ユースケースの例

例えば こんな メッセージを出す bot がいるとします *2

上記の メッセージ から UUID ( OpenStack の Server UUID ) を取り出して、別の bot で何か処理をしたい場合を考えましょう

  • text を抜き出して正規表現で UUID のマッチを試みる
  • attachments や blocks の Hash/Array をイテレートして UUID を取り出す

といったアプローチでメッセージを扱うかと思います。一種のスクレイピングですね

非構造化されたデータを扱う問題

非構造化されたデータ を扱うのは何かと面倒で変更にも弱いですね

元のメッセージのフォーマットが変わる ( text の内容が変わる, attachments や blocks の構造が変わる ) と bot を再実装する必要が出そうです 。人間向けの view に依存してるコードは実装が複雑になったり、メンテナンス性が下がります。スクレイピングのコードを書いたことのある人はよくわかるかと思います。

botbot のメッセージを扱う場合に、構造化されたデータ ( Hash, Array ) で扱えると楽だよなぁと.... 長らく思っていたのでした。

metadata を使おう

で、改めて Slack のドキュメントを調べてみたら、メッセージに metadata として Hash 構造のデータを付けられるのを この度 知ったのでした。

api.slack.com

今年の4月頃に出ていたのかな? *3

metadata の例

下記のように metadataevent_typeevent_payload を入れてメッセージを POST できる!

{
    "channel": "C23456",
    "text": "New teammate @Billy just joined",
    "metadata": {
        "event_type": "new_teammate",
        "event_payload": {
            "id": "TK-2132",
            "summary": "New teammate has been added to the channel",
            "description": "@Billy is a new teammate and needs to be added to the neccesary channels",
            "priority": "HIGH",
            "resource_ type": "TASK"
        }
    }
}

別の bot のメッセージに反応して何かする bot を作る際、metadata を参照すると構造化されたデータで扱いやすそう (ただし メッセージを出す bot で metadata を付与してないといけないけど ... )

metadata を使った、別の bot のメッセージに反応して何かする bot 実装例

  • metadata 付きのメッセージを通知する Ruby アプリ
  • metadata 付きのメッセージに反応する Python Bolt アプリ

を例に作ってみました

metadata 付きのメッセージを通知する Ruby アプリ

↑ のようなメッセージを POST する単純なコードです。metadata も付けています。

#!/usr/bin/env ruby

require 'slack-ruby-client'
require 'dotenv'

Dotenv.load

params = {
  channel: '#dev',
  username: 'live-migration-notifier',
  text: ":arrow_forward: live-migrationが開始されました",
  blocks: [
    {
      type: :section,
      text: {
        type: :plain_text,
        text: ":arrow_forward: live-migrationが開始されました",
        emoji: true
      }
    },
    {
      type: :divider,
    },
    {
      type: :context,
      elements: [
        {
          type: :mrkdwn,
          text: ":desktop_computer: *example.com*\n:pencil2: 7ef9111c-0000-0000-0000-1717950e45cf",
          verbatim: true
        }
      ]
    },
    {
      type: :context,
      elements: [
        {
          type: :plain_text,
          text: ":outbox_tray: host000",
          emoji: true
        },
        {
          type: :plain_text,
          text: ":inbox_tray: host001",
          emoji: true
        }
      ]
    },
    {
      type: :divider,
    },
    {
      type: :context,
      elements: [
        {
          type: :plain_text,
          text: "req-b23c7b63-0000-0000-0000-00000000",
          emoji: false
        },
        {
          type: :plain_text,
          text: "by instance-migrator",
          emoji: false
        }
      ]
    }
  ]
}

# こんな感じで 構造化したデータを突っ込める
metadata = {
  event_type: "live_migration_started",
  event_payload: {
    source_host: "host000",
    dest_host: "host001",
    server: {
      name: "example.com",
      uuid: "7ef9111c-0000-0000-0000-1717950e45cf",
    },
  }
}

# metadata には JSON を入れる
params[:metadata] = metadata.to_json

client = Slack::Web::Client.new(token: ENV['SLACK_API_TOKEN'])
client.chat_postMessage(params)

metadata 付きのメッセージに反応する Python Bolt アプリ

先のメッセージに反応する Python Bolt アプリです。

"""_summary_
"""
import os

import dotenv
import slack_bolt
from slack_bolt.adapter.socket_mode import SocketModeHandler

dotenv.load_dotenv()

app = slack_bolt.App(token=os.environ["SLACK_BOT_TOKEN"])


@app.event("message")
def handle_message(event, say):
    metadata = event.get("metadata")
    if metadata is None:
        return

    if metadata["event_type"] == "live_migration_started":
        payload = metadata["event_payload"]

        print(payload)

        server_name = payload["server"]["name"]
        server_uuid = payload["server"]["uuid"]
        say("{0} {1} started live-migration".format(server_name, server_uuid))


if __name__ == "__main__":
    handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
    handler.start()

動作の例

二つの bot が連携して動作している例です

Python Bolt アプリでは、以下のように metadata を受け取っています

 $ poetry run python bot.py
⚡️ Bolt app is running!
{'source_host': 'host000', 'dest_host': 'host001', 'server': {'name': 'example.com', 'uuid': '7ef9111c-0000-0000-0000-1717950e45cf'}}

意図した通りにデータを扱うことができています。

Proof Of Concent なコードなので、特に利用価値がある実装ではありません。 metadata を通して依存関係を持っている、ってのがポイントですね。

感想

他のユースケース

  • Emoji で Reaction 付けたら、メッセージの metadata ( event_type, event_payload ) を取り出して何か操作するってコード書けそう

metadata の 技術的な疑問点

  • OSS なんかで汎用的に使う bot の場合、 event_type命名はどう扱うのがいいのだろうか?
    • あまり単純な名前をつけると、別の bot と衝突するだろう
    • namespace の規約があるといいようには思う

そもそもの話

  • live-migration-notifier の通知を別チャンネルにも転送したいユースケースがあった
  • live-migration-notifier に機能を追加するよりも、別の bot で転送機能実装したらいいかと思って 「 別の bot のメッセージに反応して何かする bot 」を書いていた

*1:何らかの理由で 元のメッセージを出す bot の実装を変えたくない/変えられない場合

*2:同僚の id:buty4649 が書いた OpenStackのLiveMigrationの通知するくん のメッセージです github.com

*3: このエントリを書いている時点では open beta の機能です

lsof でファイルオープンのフラグを確認できる

ryuichi1208.hateblo.jp

↑ 同僚のエントリを読んで 早速調べものをしていたのだが、8進数で書かれたフラグを理解するのがちょっと大変。 lsof あたりで人間が読みやすくするようにサポートしてないのかな? ... と調べたらあった!

lsof +f g オプション

lsof に +f g って付ける良い。以下は apache2 の lsof をとった内容

$ sudo lsof +f g -p 5087 | tail
apache2 5087 www-data  DEL       REG                    0,5             32893 /dev/zero
apache2 5087 www-data    0r      CHR              LG    1,3      0t0        6 /dev/null
apache2 5087 www-data    1w      CHR            W,LG    1,3      0t0        6 /dev/null
apache2 5087 www-data    2w      REG         W,AP,LG  252,0      739 35389666 /var/log/apache2/error.log
apache2 5087 www-data    3u     IPv4   RW,ND,0x80000  67450      0t0      TCP *:http (LISTEN)
apache2 5087 www-data    4r     FIFO         0x80000   0,10      0t0    32892 pipe
apache2 5087 www-data    5w     FIFO       W,0x80000   0,10      0t0    32892 pipe
apache2 5087 www-data    6w      REG W,AP,LG,0x80000  252,0        0 35389607 /var/log/apache2/other_vhosts_access.log
apache2 5087 www-data    7w      REG W,AP,LG,0x80000  252,0        0 35389605 /var/log/apache2/access.log
apache2 5087 www-data    8u  a_inode      RW,0x80000   0,11        0    12000 [eventpoll]

フラグは略記されており、それぞれの読み方は man に書いてあります

       FILE-FLAG  when  g or G has been specified to +f, this field contains the contents of the f_flag[s] member of the kernel file structure and the kernel's per-process open file flags (if available); `G'
                  causes them to be displayed in hexadecimal; `g', as short-hand names; two lists may be displayed with entries separated by commas, the lists separated by a semicolon (`;'); the  first  list
                  may contain short-hand names for f_flag[s] values from the following table:

                       AIO       asynchronous I/O (e.g., FAIO)
                       AP        append
                       ASYN      asynchronous I/O (e.g., FASYNC)
                       BAS       block, test, and set in use
                       BKIU      block if in use
                       BL        use block offsets
                       BSK       block seek
                       CA        copy avoid
                       CIO       concurrent I/O
                       CLON      clone
                       CLRD      CL read
                       CR        create
                       DF        defer
                       DFI       defer IND
                       DFLU      data flush
                       DIR       direct
                       DLY       delay
                       DOCL      do clone
                       DSYN      data-only integrity
                       DTY       must be a directory
                       EVO       event only
                       EX        open for exec
                       EXCL      exclusive open
                       FSYN      synchronous writes
                       GCDF      defer during unp_gc() (AIX)
                       GCMK      mark during unp_gc() (AIX)
                       GTTY      accessed via /dev/tty
                       HUP       HUP in progress
                       KERN      kernel
                       KIOC      kernel-issued ioctl
                       LCK       has lock
                       LG        large file
                       MBLK      stream message block
                       MK        mark
                       MNT       mount
                       MSYN      multiplex synchronization
                       NATM      don't update atime
                       NB        non-blocking I/O
                       NBDR      no BDRM check
                       NBIO      SYSV non-blocking I/O
                       NBF       n-buffering in effect
                       NC        no cache
                       ND        no delay
                       NDSY      no data synchronization
                       NET       network
                       NFLK      don't follow links
                       NMFS      NM file system
                       NOTO      disable background stop
                       NSH       no share
                       NTTY      no controlling TTY
                       OLRM      OLR mirror
                       PAIO      POSIX asynchronous I/O
                       PP        POSIX pipe
                       R         read
                       RC        file and record locking cache
                       REV       revoked
                       RSH       shared read
                       RSYN      read synchronization
                       RW        read and write access
                       SL        shared lock
                       SNAP      cooked snapshot
                       SOCK      socket
                       SQSH      Sequent shared set on open
                       SQSV      Sequent SVM set on open
                       SQR       Sequent set repair on open
                       SQS1      Sequent full shared open
                       SQS2      Sequent partial shared open
                       STPI      stop I/O
                       SWR       synchronous read
                       SYN       file integrity while writing
                       TCPM      avoid TCP collision
                       TR        truncate
                       W         write
                       WKUP      parallel I/O synchronization
                       WTG       parallel I/O synchronization
                       VH        vhangup pending
                       VTXT      virtual text
                       XL        exclusive lock

                  this  list  of names was derived from F* #define's in dialect header files <fcntl.h>, <linux</fs.h>, <sys/fcntl.c>, <sys/fcntlcom.h>, and <sys/file.h>; see the lsof.h header file for a list
                  showing the correspondence between the above short-hand names and the header file definitions;

                  the second list (after the semicolon) may contain short-hand names for kernel per-process open file flags from this table:

                       ALLC      allocated
                       BR        the file has been read
                       BHUP      activity stopped by SIGHUP
                       BW        the file has been written
                       CLSG      closing
                       CX        close-on-exec (see fcntl(F_SETFD))
                       LCK       lock was applied
                       MP        memory-mapped
                       OPIP      open pending - in progress
                       RSVW      reserved wait
                       SHMT      UF_FSHMAT set (AIX)
                       USE       in use (multi-threaded)

man7.org

tig で大きいリポジトリを扱う際、高速にする設定を知る

github.com

この issue を読んでいたところ、下記のような設定がおすすめされているのを知りました

github.com

設定を追加したところ Linux カーネルリポジトリで tig を起動すると数秒〜 待たされるのが シュっと出るようになった! やったー 🐧

今までの時間を返してくれ!

tig でコミットを読んでる際に、コミットメッセージを DeepL でピっと翻訳したい

macOS の話題です

Linux カーネルリポジトリなんかでコミットメッセージをすぐ翻訳して読みたいなと思ったので、以下のように ~/.tigrc を設定してみる

設定例

ショートカットキーは、適当に 9 に bind しています

bind generic 9 @sh -c 'git log --format=%B -n1 %(commit) | pbcopy; open /Applications/DeepL.app; osascript -e \'tell application "System Events" to keystroke "v" using command down\''

利用のイメージ

さて、 tig でコミットを眺めてる時に 9 をピッと押すと

DeepL が出てきて、コミットログをペーストして翻訳して出てくる

とりあえずは便利に使えそうかな?

注意書き

  • もっと便利な方法あったら教えてください!
    • tig から起動できるなら別の翻訳アプリでも OK です。
  • DeepL の翻訳の精度については特に問わないものとします

sar -W の pswpin/s pswpout/s がどのファイルから読まれているか調べる

概要

ちょいと sar -W を使うことがあって ソースを調べた話です

sar -W とは?

sar -W で 一秒間にスワップインしたページ / スワップアウトしたページを参照できる

hiboma@example:~$ sar -W 1
Linux 5.15.0-25-generic (example)   06/20/22    _x86_64_    (4 CPU)

12:25:22     pswpin/s pswpout/s
12:25:23         0.00      0.00
12:25:24         0.00      0.00
12:25:25         0.00      0.00

以下は man の説明を引用したものです

       -W     Report swapping statistics. The following values are displayed:

              pswpin/s
                     Total number of swap pages the system brought in per second.

              pswpout/s
                     Total number of swap pages the system brought out per second.

sar -W はどのファイルから値を取っているのか?

この数値はどこから取ってるのだったかな? と気になったのでソースを調べました。

github.com

ソースを clone します

ghq get git@github.com:sysstat/sysstat.git 

pswpin でソースを grep ( ag ) して、それっぽいコードを探してアタリをつける。ヘッダファイルの 👈 っぽいですね。

 $ ag pswpin

... 略m

rd_stats.h
156:    unsigned long pswpin    __attribute__ ((aligned (8))); 👈 

pcp_stats.c
282:    snprintf(buf, sizeof(buf), "%lu", ssc->pswpin);

xml_stats.c
399:        "pswpin=\"%.2f\" "
401:        S_VALUE(ssp->pswpin,  ssc->pswpin,  itv),

ヘッダファイルを読む

rd_stats.h で struct stats_swap なる構造体を見つける。次は、この構造体が参照されている関数を探す。

/* Structure for swapping statistics */
struct stats_swap {
    unsigned long pswpin    __attribute__ ((aligned (8)));
    unsigned long pswpout   __attribute__ ((aligned (8)));
};

関数を探す

C のソースを読むときは Emacs が手に馴染んでいるので、 GNU Global で絞り込む

sscanf してるのが怪しいですね。ジャンプすると read_vmstat_swap() という関数です

read_vmstat_swap を読む

read_vmstat_swap() で pswpin / pswapout の値を取ってるみたいですね。 /proc/vmstat を読んでるようです。

/*
 ***************************************************************************
 * Read swapping statistics from /proc/vmstat.
 *
 * IN:
 * @st_swap    Structure where stats will be saved.
 *
 * OUT:
 * @st_swap    Structure with statistics.
 *
 * RETURNS:
 * 1 on success, 0 otherwise.
 ***************************************************************************
 */
__nr_t read_vmstat_swap(struct stats_swap *st_swap)
{
    FILE *fp;
    char line[128];

    if ((fp = fopen(VMSTAT, "r")) == NULL)
        return 0;

    while (fgets(line, sizeof(line), fp) != NULL) {

        if (!strncmp(line, "pswpin ", 7)) {
            /* Read number of swap pages brought in */
            sscanf(line + 7, "%lu", &st_swap->pswpin);
        }
        else if (!strncmp(line, "pswpout ", 8)) {
            /* Read number of swap pages brought out */
            sscanf(line + 8, "%lu", &st_swap->pswpout);
        }
    }

    fclose(fp);
    return 1;
}

/proc/vmstatgrep して調べてみると、あったあった

hiboma@example:~$ grep pswp /proc/vmstat
pswpin 0
pswpout 0

感想

  • 10分くらいで調べて、10分でエントリ書き下した
  • イマドキなシステムだと sar をあえて使うケースは少なくなってきてるとは思う
  • /proc から kernel の実装に飛び込んで深追いもできるので、いい材料だとは思うんす