Locustでシャーディング的なことをしたい
Locustは大変お手軽に負荷試験のスクリプトを作れる、かつ、テストスクリプトの柔軟性があってよい。
今回は以下のような要件があった。
- 複数のWorkerで処理をスケールアウトさせたい
- あるIDは、同じWorkerで処理をさせたい(同じIDを別のWorkerで処理させたくない)
Out of boxなサポートはないものの、Locustの Custom Messageのサンプル がほぼこれに該当する。
@events.test_start.add_listener
のリスナーでWorkerにIDを分配する- このタイミングまでには、Master, Workerともに立ち上がっているので、Master側はいくつWorkerを持っているか把握している
environment.runner.worker_count
およびenvironment.runner.clients
を参照すればOK
- 各Worker向けに、処理すべきIDをMessage経由で頒布すればOK
- これを応用して、ID自体ではなくて、処理すべきIDのレンジを渡す、とかもできそう
@events.test_start.add_listener def on_test_start(environment, **_kwargs): # When the test is started, evenly divides list between # worker nodes to ensure unique data across threads if not isinstance(environment.runner, WorkerRunner): users = [] for i in range(environment.runner.target_user_count): users.append({"name": f"User{i}"}) worker_count = environment.runner.worker_count chunk_size = int(len(users) / worker_count) for i, worker in enumerate(environment.runner.clients): start_index = i * chunk_size if i + 1 < worker_count: end_index = start_index + chunk_size else: end_index = len(users) data = users[start_index:end_index] environment.runner.send_message("test_users", data, worker)
IntelliJ IDEA x WSL2がクソ重い件
困ったこと
原因
Windows Defenderが悪さしていたっぽい。以下のJetBrainsのForumも結構荒れていた模様。
https://youtrack.jetbrains.com/issue/IDEA-286059
対応
Forumのコメントにあったプロセスの除外設定だけでは症状は改善せず、こちらのGistの内容でガバっと指定する必要があった。
使ってるDistroに応じて指定するパスは変わってくるので要注意。
MySQL + JDBC Timezoneまとめ
前回からの続き。JDBCでのデータ書き込み、読み出しの際のタイムゾーン周りを確認する。ややこしくて頭が爆発しそう。
前提条件
- MySQL 5.7
- MySQLのSystem time zoneはUTC+07:00 (Time zone = Asia/Bangkok) で固定
- MySQL Connector/J 5.1 を利用
前提知識
- JDBCでMySQLの
DATETIME
ないしTIMESTAMP
型に値を渡す場合、Java側はjava.sql.Timestamp
の値を用いる。 - JDBCの接続文字列で指定可能なオプションの一つに
useLegacyDatetimeCode
があり、この値によって挙動が変わる。
実験
以下の2点のパラメータをいじりながら、データの書き込み、読み込みを観測する。
書き込む値は前回と同じ、 '2021-02-02T12:00:00+00:00' とする。 java.sql.Timestamp
に変換する際は、 java.text.SimpleDateFormat
をUTCで用いる。
結果
- MySQL側に保存する時刻のタイムゾーンを統一するときは
useLegacyDatetimeCode=false
を指定する。統一せず、個別のレコードごとにタイムゾーンを管理する場合は、useLegacyDatetimeCode=true
でよい。 useLegacyDatetimeCode=false
を指定すると、java.sql.Timestamp
の値をUTCとして捉えて、MySQLのSystem time zoneでの時刻に変換をかける。useLegacyDatetimeCode=true
(あるいは未指定=デフォルト値でtrue) の場合、 Java側のタイムゾーンでjava.sql.Timestamp
を変換した時刻を保存する。
id | system_tz | session_tz | raw_datetime | legacy_code | datetime | timestamp |
---|---|---|---|---|---|---|
1 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 12:00:00 |
2 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 19:00:00 |
3 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 08:00:00 |
4 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | TRUE | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 |
5 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | TRUE | 2021-02-02 12:00:00 | 2021-02-02 12:00:00 |
6 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | TRUE | 2021-02-02 23:00:00 | 2021-02-02 23:00:00 |
7 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | FALSE | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 |
8 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | FALSE | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 |
9 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | FALSE | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 |
mysql> SELECT @@system_time_zone, @@session.time_zone; +--------------------+---------------------+ | @@system_time_zone | @@session.time_zone | +--------------------+---------------------+ | +07 | SYSTEM | +--------------------+---------------------+ 1 row in set (0.00 sec) mysql> SELECT id, system_tz, session_tz, raw_datetime, (jdbc_param NOT LIKE '%useLegacyDatetimeCode=false') as legacy_jdbc, datetime, timestamp FROM tz_sample; +----+--------------+------------------+---------------------------+-------------+---------------------+---------------------+ | id | system_tz | session_tz | raw_datetime | legacy_jdbc | datetime | timestamp | +----+--------------+------------------+---------------------------+-------------+---------------------+---------------------+ | 1 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 12:00:00 | | 2 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 19:00:00 | | 3 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | NULL | 2021-02-02 12:00:00 | 2021-02-12 08:00:00 | | 4 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | 1 | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 | | 5 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | 1 | 2021-02-02 12:00:00 | 2021-02-02 12:00:00 | | 6 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | 1 | 2021-02-02 23:00:00 | 2021-02-02 23:00:00 | | 7 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | 0 | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 | | 8 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | 0 | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 | | 9 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | 0 | 2021-02-02 19:00:00 | 2021-02-02 19:00:00 | +----+--------------+------------------+---------------------------+-------------+---------------------+---------------------+ 9 rows in set (0.00 sec)
その他まとめ
JDBCの接続作成
String defaultTimeZone = "Australia/Sydney"; TimeZone.setDefault(TimeZone.getTimeZone(defaultTimeZone)); Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection conn = DriverManager.getConnection(CONNECTION_STRING_BASE + "&useLegacyDatetimeCode=false");
データ書き込み
日付はRFC3339形式のUTCでもらう想定。 SimpleDateFormat
で文字列をパースする前に、タイムゾーンを設定しておく。
private static void insertItem(String datetimeInRFC3339, TimeZone tz, Connection conn) throws ParseException, SQLException { SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+00:00"); rfc3339.setTimeZone(TimeZone.getTimeZone("UTC")); long epoch = rfc3339.parse(datetimeInRFC3339).getTime(); // Get the epoch seconds in UTC Timestamp ts = new Timestamp(epoch); PreparedStatement pstmt = conn.prepareStatement("INSERT INTO tz_sample" + "(system_tz, session_tz, raw_datetime, jdbc_param, datetime, timestamp)" + "VALUES (?, ?, ?, ?, ?, ?)"); pstmt.setString(1, "Asia/Bangkok"); pstmt.setString(2, tz.getID()); pstmt.setString(3, datetimeInRFC3339); pstmt.setString(4, conn.getMetaData().getURL()); pstmt.setTimestamp(5, ts); pstmt.setTimestamp(6, ts); pstmt.execute(); }
データ読み込み
Epoch秒も一緒に表示しておく。
private static String getOneRow(ResultSet rs) throws SQLException { String systemTZ = rs.getString("system_tz"); String sessionTZ = rs.getString("session_tz"); String rawDatetime = rs.getString("raw_datetime"); String jdbcParam = rs.getString("jdbc_param"); Date datetime = rs.getDate("datetime"); Timestamp timestamp = rs.getTimestamp("timestamp"); int rowNum = rs.getRow(); Object[] params = {rowNum, systemTZ, sessionTZ, rawDatetime, jdbcParam, dateToStr(datetime), timestamp.toString(), timestamp.getTime() / 1000}; return FMT.format(params); } private static String dateToStr(Date date) { return dateToStr(date, TimeZone.getDefault()); } private static String dateToStr(Date date, String tzString) { TimeZone tz = TimeZone.getTimeZone(ZoneId.of(tzString)); return dateToStr(date, tz); } private static String dateToStr(Date date, TimeZone tz) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss"); sdf.setTimeZone(tz); return sdf.format(date); }
MySQL Timezoneまとめ
やりたいこと
前提知識
MySQLサーバ側のタイムゾーンの概念
- System time zone: サーバ起動時に指定するタイムゾーン。基本的に不変。使い所よくわかんない。
- Server current time zone: サーバが現時点で使ってるタイムゾーン。デフォルトではSystem time zoneと同じ。
- 以下のいずれかの方法で変更可能
- MySQLサーバ起動時に
--default-time-zone='Asia/Bangkok'
のように指定する - 管理者権限(
SYSTEM_VARIABLES_ADMIN
権限)でSET GLOBAL time_zone = 'Aisa/Bangkok'
のように指定する
- MySQLサーバ起動時に
- 以下のいずれかの方法で変更可能
- Per-session time zone: Sessionごとのタイムゾーン。
SET time_zone = 'Asia/Bangkok'
のように変更可能
参考
データ型 DATETIME
と TIMESTAMP
DATETIME
: 日時を持つ。基本的にタイムゾーンの概念はない。TIMESTAMP
: 内部的にEpoch秒を持つ。以下の特徴を持つ。
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.) By default, the current time zone for each connection is the server's time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable. For more information, see Section 5.1.15, “MySQL Server Time Zone Support”.
参考
実験
前提条件
対応内容
以下のようなテーブルを作り、セッションおよびJDBCのパラメータを変えながらタイムスタンプの値を見ていく。
mysql> CREATE TABLE tz_sample -> ( -> id INT AUTO_INCREMENT PRIMARY KEY, -> system_tz VARCHAR(256), -> session_tz VARCHAR(256), -> raw_datetime VARCHAR(256), -> jdbc_param TEXT, -> datetime DATETIME, -> timestamp TIMESTAMP -> ); Query OK, 0 rows affected (0.04 sec)
INSERT文は基本的に以下で固定。
INSERT INTO tz_sample (system_tz, session_tz, raw_datetime, jdbc_param, datetime, timestamp) VALUES ('Asia/Bangkok', 'そのときのセッションのタイムゾーン', '2021-02-02T12:00:00+00:00', '2021/02/02 12:00:00', '2021/02/02 12:00:00');
確認項目
データ確認
TIMESTAMP
型に日時を保存する際にタイムゾーンを明示しない場合、現在のセッションのタイムゾーンの日時として認識される。- '12:00' だと、タイ時間の昼12時 = UTCの朝5時となる。
TIMESTAMP
型は常にUTCで時刻を保存している。このため、データを読み出すときは、セッションのタイムゾーンによって表示される時刻が変わる。DATETIME
型はタイムゾーン情報を持たない。よって、セッションのタイムゾーンを変更しても、表示される時刻は同じ。
Timezone = Asia/Bangkokで読み出す
mysql> SELECT @@system_time_zone, @@session.time_zone; +--------------------+---------------------+ | @@system_time_zone | @@session.time_zone | +--------------------+---------------------+ | +07 | SYSTEM | +--------------------+---------------------+ 1 row in set (0.00 sec) mysql> SELECT id, system_tz, session_tz, raw_datetime, datetime, timestamp FROM tz_sample WHERE jdbc_param IS NULL; +----+--------------+------------------+---------------------------+---------------------+---------------------+ | id | system_tz | session_tz | raw_datetime | datetime | timestamp | +----+--------------+------------------+---------------------------+---------------------+---------------------+ | 1 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 12:00:00 | | 2 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 19:00:00 | | 3 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 08:00:00 | +----+--------------+------------------+---------------------------+---------------------+---------------------+
Timezone = UTCで読み出す
mysql> SET time_zone = 'UTC'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT id, system_tz, session_tz, raw_datetime, datetime, timestamp FROM tz_sample WHERE jdbc_param IS NULL; +----+--------------+------------------+---------------------------+---------------------+---------------------+ | id | system_tz | session_tz | raw_datetime | datetime | timestamp | +----+--------------+------------------+---------------------------+---------------------+---------------------+ | 1 | Asia/Bangkok | Asia/Bangkok | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 05:00:00 | | 2 | Asia/Bangkok | UTC | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 12:00:00 | | 3 | Asia/Bangkok | Australia/Sydney | 2021-02-02T12:00:00+00:00 | 2021-02-02 12:00:00 | 2021-02-12 01:00:00 | +----+--------------+------------------+---------------------------+---------------------+---------------------+ 3 rows in set (0.00 sec)
Admin権限なしでRustをWindowsにインストール / Installing Rust on Windows without Admin
Why
- Rust製の諸々のツールを使いたい
- 通常、RustをWindowsにインストールするときは管理者権限必要
- 会社用のPCとかだと、諸々あってAdmin権限がない
How
- scoop をインストール
scoop install msys2
でMSYS2をインストール。これでMinGW64が勝手に入る。scoop install gcc
は、メンテナンスされていないようでインストールに失敗するので要注意scoop install rustup
でRustupをインストール。多分これは公式サイトからダウンロードしたものでもOKなはず(試してない)rustup default stable-x86_64-pc-windows-gnu
で、MinGW64を使うよう設定- 一部のツールは
gcc
が必要なので、MSYS2側でインストールしてPATHを通しておく。 - 完了!
参考
Why
- I wanted to use fancy tools made by Rust (like
bat
orfd
) - Usually, the Admin privilege is required to install Rust on Windows
- I don't have the Admin privilege of some laptop
How
- Install scoop
- Install msys2 via
scoop install msys2
. This will install MinGW64 as a part of msys2.- Some old articles mention
scoop install gcc
, but it seems this package is no longer maintained and fails to install
- Some old articles mention
- Install
rustup
viascoop install rustup
. Maybe we can download the official binary. - Configure rustup to use MinGW64 with
rustup default stable-x86_64-pc-windows-gnu
. - DONE!
Reference
ES6の書式でJestでテストする (Bebelなしで)
やりたいこと
- Jest を使ったテストを導入したい
- ただ、
require
じゃなくてimport
とかのES6の構文を使いたい - Babelの設定とかで消耗したくない
注意事項
JestのES Modulesサポートはまだ実験段階で、サポートされてない機能が結構あるらしい。
やり方
jest
の導入
普通に npm
でインストールする。 --save-dev
で開発時のみ必要なDependencyであることを明示する。
npm install jest --save-dev
package.json
の編集
"scripts": { "test": "..." }
部分の編集"jest" { "transform": {} }
の追加
がポイント
{ "name": "paiza", "version": "0.0.1", "description": "", "main": "index.js", "type": "module", "scripts": { "test": "node --experimental-vm-modules node_modules/.bin/jest" }, "author": "", "license": "ISC", "devDependencies": { "jest": "^26.6.3" }, "jest": { "transform": {} } }
transformの無効化
ドキュメントによれば、
- コードの変換を無効化する (->
transform: {}
を指定する) - 変換先をCommonJSじゃなくてESMにする
のいずれかが必要。今回できればBabelを使いたくないので、一つ目を採用。
nodeの --experimental-vm-modules
フラグの追加
そもそも node
自体に --experimental-vm-modules
フラグを渡さないとES Modulesの import
を認識してくれない。
よって、テストを実行する際にこのパラメータを加えておく。
参照
基本的に以下のドキュメントに従えばOK。
Vivaldi: YouTube MusicをWeb Panelに追加する
事象
Web PanelにYouTube Musicを追加する。再生した後にWeb Panelを閉じると、再生が止まってしまう。
原因
- デフォルトでモバイル版のサイトがWeb Panelに登録される
- モバイル版は、フォーカスを失うと再生が止まる仕様っぽい
対処
- パネルのアイコンを右クリックして、「デスクトップ版を表示」を選べばOK