Pixela の唯一の非公式 CLI ツール pa が v1.6.0 にバージョンアップしました。
Pixela の CLI ツール pa が v1.6.0 にバージョンアップしました。
v1.6.0 アップデート内容
Pixela API の HTTP レスポンスステータスコードを表示するようになっています。
Pixela API を呼び出したのがエラーになるときに HTTP レスポンスステータスコードが 4xx
だったらエラーの原因がクライアント側にあることが分かりますし、HTTP ステータスコードが 5xx
だったらエラーの原因がサーバー側にあることが一目で分かります。
API 呼び出しのエラーの原因がクライアント側にあるならリトライをしてもまたエラーになりそうとか、エラーの原因がサーバー側にあるなら API 呼び出しをリトライするのを検討できそう、みたいに判断する材料になりますね。
$ pa pixel update --graph-id=<graph-id> --date=<date> --quantity=<quantity> {"message":"Success.","isSuccess":true,"statusCode":200}
現場からは以上です。
MySQL のテーブルの概算の行数を INFORMATION_SCHEMA.TABLES から素早く取得する方法。
はじめに
MySQL のテーブルの概算の行数は INFORMATION_SCHEMA.TABLES
テーブルから取得すると素早く取得できる。
RDBMS のテーブルの行数は SELECT COUNT(*) FROM <テーブル名>
クエリで取得できるけど、テーブルのレコードが何十億レコードもあるなど非常に多いときはそれなりに時間がかかる。
MySQL はデータベースのメタデータを格納する INFORMATION_SCHEMA
データベースがあって INFORMATION_SCHEMA
データベース の TABLES
テーブルから概算ではあるけどテーブルの行数を非常に素早く取得できる。
INFORMATION_SCHEMA.TABLES から行数を取得する方法
INFORMATION_SCHEMA.TABLES
テーブルの TABLE_ROWS
カラムがテーブルの行数。
TABLE_SCHEMA
カラムと TABLE_NAME
カラムとで行数を取得したいテーブルを絞り込める。
mysql> SELECT TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='<データベース名>' AND TABLE_NAME='<テーブル名>';
INFORMATION_SCHEMA.TABLES から行数を取得するときに気をつけること
INFORMATION_SCHEMA.TABLES
テーブルの TABLE_ROWS
カラムから取得できる行数は正確ではなく概算にすぎないことには気をつけておく。
MySQL :: MySQL 8.0 リファレンスマニュアル :: 26.38 INFORMATION_SCHEMA TABLES テーブル
行数。 MyISAM などの一部のストレージエンジンは、正確な数を格納します。 InnoDB などのほかのストレージエンジンの場合、この値は概算であり、実際の値と 40% から 50% まで異なる可能性があります。 このような場合、正確な数を取得するには SELECT COUNT(*) を使用します。
TABLE_ROWS は、INFORMATION_SCHEMA テーブル用の NULL です。
nnoDB テーブルの場合、行カウントは SQL 最適化で使用される単なる概算です。 (InnoDB テーブルがパーティション化されている場合も、これは当てはまります。)
INFORMATION_SCHEMA.TABLES から行数を取得する例
実際に INFORMATION_SCHEMA.TABLES
テーブルからテーブルの行数を取得してみる。
MySQL の world
データベースの country
テーブルの行数を取得していく。
country
テーブルから SELECT COUNT(*)
で取得する結果は 239 になって INFORMATION_SCHEMA.TABLES
テーブルから取得する結果も 239 になっている (この例ではたまたま2つの方法で取得する行数が一致している)
mysql> source /path/to/world.sql; Query OK, 0 rows affected (0.00 sec) mysql> SELECT COUNT(*) FROM world.country; +----------+ | count(*) | +----------+ | 239 | +----------+ 1 row in set (0.01 sec) mysql> SELECT TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='world' AND TABLE_NAME='country'; +------------+ | TABLE_ROWS | +------------+ | 239 | +------------+ 1 row in set (0.00 sec)
参考サイト
Arch Linux でジョブをスケジュール実行するには systemd のタイマーが使える。
はじめに
Arch Linux でジョブをスケジュール実行するには systemd のタイマーが使える。systemd のタイマーを使ってジョブをスケジュール実行する方法をメモしておく。
自分はジョブをスケジュール実行するときは cron を使うことが多いけど Arch Linux はデフォルトでは cron はインストールされない。 cron は cronie とかの実装があるので自分でインストールしたら使えるようになるけど、systemd のタイマーを使うと簡単にジョブをスケジュール実行することができる。
systemd のタイマーの使い方
systemd のタイマーを使うにはサービスのユニットファイルとタイマーのユニットファイルを作っていく。
とりあえずサービスのユニットファイルは hello world
を /tmp/helloworld.log
に追記して、start
と end
を標準出力に出力するだけのシンプルなものに。
あとサーブスのユニットは有効にする必要はないので [Install]
セクションは記載しなくてよい (有効にするのはタイマーのユニットなので)
$ cat /etc/systemd/system/helloworld.service [Unit] Description=hello world RefuseManualStart=no RefuseManualStop=no [Service] Type=oneshot ExecStart=/usr/bin/bash -c 'echo start; echo "hello world" >> /home/svc1/helloworld.log; echo end'
タイマーのユニットファイルはこんな感じに記載する。
タイマーのユニットファイルは拡張子を .timer
にしてサービスのユニットファイルは拡張子を .service
にするけど、2つのユニットファイルのベースファイル名は同じにしておく。
OnCalendar=minutely
を指定してタイマーが1分ごとに動くようにしている。
タイマーのオプションは systemd.timer(5) に、あとカレンダーイベントとタイムスパンの構文は systemd.time(7) に書いてある。
$ cat /etc/systemd/system/helloworld.timer [Unit] Description=Run Hello World minutely [Timer] OnCalendar=minutely Persistent=true [Install] WantedBy=timers.target
ユニットファイルを作成したらタイマーのユニットを開始と自動起動を有効にする。
# タイマーのユニットを開始する $ sudo systemctl start helloworld.timer # タイマーのユニットの自動起動を有効にする $ sudo systemctl enable helloworld.timer Created symlink /etc/systemd/system/timers.target.wants/helloworld.timer → /etc/systemd/system/helloworld.timer. $ systemctl status helloworld.timer ● helloworld.timer - Run Hello World minutely Loaded: loaded (/etc/systemd/system/helloworld.timer; disabled; vendor preset: disabled) Active: active (running) since Thu 2021-09-16 21:00:32 JST; 32s ago Trigger: n/a Triggers: ● helloworld.service Sep 16 21:00:32 localhost systemd[1]: Started Run Hello World minutely.
/tmp/helloworld.log
を見てると1分ごとに hello world
が追記していく。
タイマーのユニットがちゃんと1分ごとにサービスのユニットを起動している。
$ tail -f /tmp/helloworld.log hello world hello world hello world hello world
journalctl
コマンドを実行するとサービスのログを表示する。
$ journalctl -e -u helloworld.service Sep 16 21:01:41 localhost systemd[1]: Starting hello world... Sep 16 21:01:41 localhost bash[1999]: start Sep 16 21:01:41 localhost bash[1999]: end Sep 16 21:01:41 localhost systemd[1]: helloworld.service: Deactivated successfully. Sep 16 21:01:41 localhost systemd[1]: Finished hello world.
ユニットファイルの設定を変えたら sudo systemctl daemon-reload
コマンドを実行して設定を反映するのは普通のサービスと同じ。
$ sudo systemctl daemon-reload
タイマーの一覧を表示する
起動しているタイマーの一覧は systemctl list-timers
コマンドを実行して表示する。
$ systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES > Thu 2021-09-16 20:55:00 JST 8s left Thu 2021-09-16 20:54:05 JST 46s ago helloworld.timer helloworld.> Fri 2021-09-17 00:00:00 JST 3h 5min left Thu 2021-09-16 11:15:00 JST 9h ago shadow.timer shadow.serv> Fri 2021-09-17 20:20:44 JST 23h left Thu 2021-09-16 20:20:44 JST 34min ago systemd-tmpfiles-clean.timer systemd-tmp> 3 timers listed. Pass --all to see loaded but inactive timers, too.
参考サイト
シェルでセマンティックバージョンをソートする Go のコードスニペットのメモ。
たまにシェルでセマンティックバージョンをソートすることがあるので Go でのコードスニペットをメモしておく。
コードはこんな感じ。エラー処理は適当。
package main import ( "bufio" "fmt" "os" "sort" "github.com/Masterminds/semver" ) func main() { var a []string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { s := scanner.Text() a = append(a, s) } if err := scanner.Err(); err != nil { panic(err) } if len(a) == 0 { return } versions := make([]*semver.Version, len(a)) for i, s := range a{ v, err := semver.NewVersion(s) if err != nil { panic(err) } versions[i] = v } sort.Sort(semver.Collection(versions)) for _, v := range versions { fmt.Println(v) } }
こんな感じで使う。
$ cat versions.txt 1.0.0 1.0.0-beta.1 1.0.0-rc.1 1.0.0-alpha.1 $ cat versions.txt | go run main.go 1.0.0-alpha.1 1.0.0-beta.1 1.0.0-rc.1 1.0.0
シェル力が圧倒的に足りてないのでシェル力を養うために第13回シェル芸勉強会の問題に挑戦してみた。
シェル力が圧倒的に足りてないのでシェル力を養うために第13回シェル芸勉強会の問題に挑戦してみた。
問題はすごく工夫してあってパズル感覚で解けて楽しいしシェル力を養うのにも役立ちそう。
自分が使ってる zsh のバージョン。
$ zsh --version zsh 5.8 (x86_64-apple-darwin19.6.0)
はじめに
自分はシェル力が圧倒的に足りてないので勉強のためにシェル芸勉強会の問題に挑戦してみた。
一応自分がシェルを使う用途を書いておくと普段の仕事は Mac を使ってやっててちょっとしたことをやるのにちょろっとシェルを使う程度。
ちなみにシェル芸ってなに? ってところは Wikipedia の USP 友の会 から引用しておく。
シェル芸とは、主にUNIX系オペレーティングシステムにおいて「マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI端末へのコマンド入力一撃で終わらせること」(USP友の会会長・上田隆一による定義[9])である。この技術を持つ人物を指すシェル芸人という呼び方も存在する[10]。
古典的には、UNIX系OSの各種CUIシェル上で単一または複数のコマンドを一行のコマンドラインでをつなぎ合わせて処理をおこなう、いわゆる「ワンライナー」系の手順にあたる。
第13回シェル芸勉強会の問題に挑戦してみた
だいぶ前だけど第12回シェル芸勉強会の問題に挑戦してみたので今回は 第13回シェル芸勉強会 の問題に挑戦してみた。
Q1
Question
次のようにShift JISのファイルを作り、Shift JISで「きく」と書いてあるファイルを探すワンライナーを考えてください。(答えは「b」ですね。)
uedambp:q1 ueda$ echo あいうえお | nkf -xLws > a uedambp:q1 ueda$ echo かきくけこ | nkf -xLws > b uedambp:q1 ueda$ echo さしすせそ | nkf -xLws > c
Answer
「きく」を検索するファイルは Shift_JIS でエンコーディングしているのを UTF-8 に変換して grep してみた。
$ ls | while read f; do cat $f | nkf -Sw | grep 'きく' >/dev/null && echo $f; done
grep の出力は /dev/null
にリダイレクトして捨てるようにしてるけど、解答例を見たら grep の -q
オプションを使ってた。
便利そうなので覚えておく。
ls | while read f; do cat $f | nkf -Sw | grep -q 'きく' && echo $f; done b
Q2
Question
次のようにディレクトリa,b,c,dに1,2,...,9というファイルがあります。各ディレクトリ内のファイル数をワンライナーで数えてください。
uedambp:q2 ueda$ tree . ├── a │ ├── 1 │ ├── 2 │ └── 3 ├── b │ ├── 4 │ └── 5 ├── c └── d ├── 6 ├── 7 ├── 8 └── 9
Answer
$ ls | while read f; do echo "$f $(ls $f | xargs -n1 | wc -l)"; done a/ 3 b/ 2 c/ 0 d/ 4
Q3
Question
今度は次のような配置でファイル1,2,...,9が置かれているときに、ワンライナーでa、cの下のファイルの総数をカウントしてください(ディレクトリを除く)。つまりaなら5個、cなら4個が正解です。
uedambp:q3 ueda$ tree . ├── a │ ├── 1 │ ├── 2 │ ├── 3 │ └── b │ ├── 4 │ └── 5 └── c └── d ├── 6 ├── 7 ├── 8 └── 9
Answer
find コマンドを -type f
オプションを指定してファイルだけ検索して uniq -c
コマンドでディレクトリごとのファイルの数を数える感じ。
$ ls | while read f; do find $f -type f | awk -F/ '{print $1}' | uniq -c; done 5 a 4 c
こちらは解答例。自分は ls コマンドを余分に実行しちゃってるけど解答例のほうは無駄がなくて分かりやすくてタイプ数も少ない。
$ find . -type f | awk -F/ '{print $2}' | uniq -c 5 a 4 c
Q4
Question
まず、次のように8桁日付のファイルを作ります。 曜日別にディレクトリを作り、その中に当該するファイルを放り込んでください。
uedambp:q4 ueda$ seq -w 1 31 | xargs -I@ touch 201401@ uedambp:q4 ueda$ ls 20140101 20140107 20140113 20140119 20140125 20140131 20140102 20140108 20140114 20140120 20140126 20140103 20140109 20140115 20140121 20140127 20140104 20140110 20140116 20140122 20140128 20140105 20140111 20140117 20140123 20140129 20140106 20140112 20140118 20140124 20140130
Answer
$ ls | while read f; do wd=$(echo $f | LANG=C gdate -f - '+%a'); mkdir $wd; mv $f $wd; done
Q5
Question
以下のようにa,b,cというディレクトリを作り、その下に「{a,b,c}数字」というファイルを作ります。ファイル名の1文字目とディレクトリ名が一致するようにファイルを移動してください。
uedambp:q5 ueda$ tree . ├── a │ ├── a01 │ └── b01 ├── b │ ├── a02 │ ├── a03 │ └── c01 └── c └── a04
Answer
$ find . -type f | awk -F/ '{print substr($3,1,1), $0}' | while read d f; do mv $f $d; done $ tree . ├── a │ ├── a01 │ ├── a02 │ ├── a03 │ └── a04 ├── b │ └── b01 └── c └── c01
Q6
Answer
次のようにディレクトリa, b, cの下に、8桁日付のファイルをいくつか置きます。 各ディレクトリの最新日付のファイルをカレントディレクトリ(a,b,cのあるディレクトリ)にコピーしてください。各ディレクトリの最新ファイルの日付はそれぞれ違い、コピーの際に衝突しないこととします。
uedambp:q6 ueda$ tree . ├── a │ ├── 20130120 │ ├── 20140901 │ └── 20141021 ├── b │ ├── 20131011 │ └── 20140202 └── c ├── 20110202 ├── 20130224 └── 20141224
Answer
ls コマンドはファイル名をソートしてから出力するので各ディレクトリを ls して最後の出力が最新日付のファイル。 tail コマンドで ls コマンドの最後の出力だけを取得してコピーする。
$ ls | while read f; do ls $f | tail -n 1 | xargs -I{} cp "$f{}" .; done $ ls 20140202 20141021 20141224 a/ b/ c/
Q7
Question
Q6について、適当にファイルをtouchします。今度はタイムスタンプが最新のファイルを、a, b, cそれぞれからカレントディレクトリにコピーしてください。コピーの際にタイムスタンプを変えない事。
Answer
適当にファイルを touch する。
$ touch a/20140901 $ touch b/20131011 $ touch c/20141224
ls コマンドの -t
オプションと cp コマンドの -p
オプションを使う。
自分はどちらのコマンドもたしかいい感じのオプションがある気がするけどよく分からないので manual を見ながらやった。
$ ls | while read f; do ls -t $f | head -n 1 | xargs -I{} cp -p "$f{}" .; done
Q8
次のように5個ファイルを作ります。file1をfile2, file2をfile3, file3をfile4, file4をfile5, file5をfile1にmvしてください。
uedambp:q8 ueda$ for i in 1 2 3 4 5 ; do echo $i > file$i ; done uedambp:q8 ueda$ head * ==> file1 <== 1 ==> file2 <== 2 ==> file3 <== 3 ==> file4 <== 4 ==> file5 <== 5
Answer
コピー元とコピー先のファイルが循環するのがポイント。
$ ls | sort -r | awk 'BEGIN{d="tmp"}{print $1,d;d=$1}END{print "tmp",d}' | while read s d; do mv $s $d; done $ head * ==> file1 <== 5 ==> file2 <== 1 ==> file3 <== 2 ==> file4 <== 3 ==> file5 <== 4
参考サイト
Arch Linux のパッケージのダウングレードは downgrade コマンドを使うとすごく楽。
はじめに
Arch Linux のパッケージは pacman で管理するけどダウングレードは downgrade コマンドを使うとすごく楽なのでメモしておく。
downgrade コマンドを使わなくても自分で古いパッケージを Arch Linux Archive から検索とダウンロードしてダウングレードすることもできるけど、個人的には downgrade コマンドを使うのがずっと楽だと感じてる。
downgrade のインストール方法
AUR のパッケージは base-devel グループがインストールされているのを前提としているので先にインストールしておく。
$ sudo pacman -S --needed base-devel
downgrade のリポジトリをクローンして makepkg -si
コマンドを実行すると、makepkg
コマンドが自動でソースコードをダウンロード、pacman で依存関係を解決、コンパイルしてパッケージ化してパッケージをインストールする。
$ git clone https://aur.archlinux.org/downgrade.git $ cd downgrade $ makepkg -si
downgrade の使い方
パッケージのダウングレードは downgrade <パッケージ>
コマンドでやることができる。
downgrade コマンドを試すために pacman で Go の最新版をインストールしておいて、このあと downgrade していく。
go version
コマンドを実行すると Go のバージョンが 1.16.6 であることが表示する。
$ go version go version go1.16.6 linux/amd64
sudo downgrade go
を実行するとダウングレード先のバージョンが表示してどのバージョンにダウングレードするかのプロンプトが表示する。
$ sudo downgrade go Available packages (core): 1) go 2 1.11.5 1 remote 2) go 2 1.11.5 2 remote 3) go 2 1.11.5 3 remote # 省略 44) go 2 1.16.3 1 remote 45) go 2 1.16.4 1 remote 46) go 2 1.16.5 1 remote + 47) go 2 1.16.6 1 remote + 48) go 2 1.16.6 1 /var/cache/pacman/pkg select a package by number:
今回は Go の 1.16.6 の一つ前のバージョンの 1.16.5 にダウングレードしていく。
1.16.5 のところの左側に表示している 46
を入力して enter キーを押すとパッケージの取得が始まるって処理を継続してよいかのプロンプトが表示する。
処理を継続してよいなら Y
を入力していく。
46) go 2 1.16.5 1 remote + 47) go 2 1.16.6 1 remote + 48) go 2 1.16.6 1 /var/cache/pacman/pkg select a package by number: 46 :: Retrieving packages... go-2:1.16.5-1-x86_64 14.1 MiB 2.88 MiB/s 00:41 [####------------------------------------] 10% loading packages... warning: downgrading package go (2:1.16.6-1 => 2:1.16.5-1) resolving dependencies... looking for conflicting packages...
ダウングレードが終わると Go のパッケージを IgnorePkg に追加するかのプロンプトが表示する。 IgnorePkg に追加するなら y
を入力して IgnorePkg に追加しないなら N
を入力する。
(1/1) checking keys in keyring [########################################] 100% (1/1) checking package integrity [########################################] 100% (1/1) loading package files [########################################] 100% (1/1) checking for file conflicts [########################################] 100% :: Processing package changes... (1/1) downgrading go [########################################] 100% :: Running post-transaction hooks... (1/1) Arming ConditionNeedsUpdate... add go to IgnorePkg? [y/N] N
ダウングレードが終わってから go version
コマンドを実行すると Go のバージョンが 1.16.5 であることが表示する。
downgrade コマンドを使うとこんな感じにパッケージをダウングレードできるので、自分で古いパッケージを Arch Linux Archive から検索とダウンロードして……とやるよりもずっと楽にダウングレードができる。
$ go version go version go1.16.5 linux/amd64
参考サイト
巨大なファイルを S3 にアップロードするのが `Part number must be an integer between 1 and 10000, inclusive` のエラーになるときにやったことのメモ。
巨大なファイルを S3 にアップロードするのが Part number must be an integer between 1 and 10000, inclusive
のエラーになるときにやったことをメモしておく。
なにをしたらどんなことがおきるか
100GB くらいのファイルを aws s3 cp
コマンドで S3 にアップロードしたらこんなエラーになった。
An error occurred (InvalidArgument) when calling the UploadPart operation: Part number must be an integer between 1 and 10000, inclusive
$ aws --version
どうしたか
aws s3 cp
の公式ドキュメントを見てみた。
aws s3 cp
の公式ドキュメントは「ストリームを S3 にアップロードしててストリームのサイズが 50GB より大きい場合は ストリームの予想サイズを ----expected-size
オプションに指定しないと失敗することがあるよ」と書いているので、 --expected-size= 107374182400
みたいに --expected-size
を指定したらエラーにならなくなった。
たぶんだけどストリームを S3 にアップロードするときはチャンク数が最大でも 10000 の制限がありそう。 --expected-size
オプションでストリームの予想サイズを指定するとストリームをチャンクに分割するときにチャンク数が 10000 を超えないようにチャンクサイズを決めてそう。
This argument specifies the expected size of a stream in terms of bytes. Note that this argument is needed only when a stream is being uploaded to s3 and the size is larger than 50GB. Failure to include this argument under these conditions may result in a failed upload due to too many parts in upload.