Vue3でaxiosをMock化する

  • このエントリーをはてなブックマークに追加

APIのアクセスはaxiosでコールするけど、開発中はMockに差し替えるということをします。

mockにはaxios-mock-adapterを利用します。

mock.js
import MockAdapter from 'axios-mock-adapter'
const data = { world: "world" }
export default {
run(client) {
const mock = new MockAdapter(client, {delayResponse: 300})
mock.onGet("/api/hello").reply(200, data)
}
}

delayResponseを設定することで意図的に遅くすることができます。

mockの色々な書き方

直接JSONを返す

mock.onGet("/api/hello").reply(200, { hello: "world" })

関数の実行

mock.onGet("/api/hello").reply((config) => {
return [200, { hello: "world" }];
});

Queryの処理

ややこしいのは、config.paramsではないと言うことです。
/api/hello?name=taroとリクエストする場合は、正規表現で/\/api\/hello\?.*/と指定する必要があります。ここは良い感じに処理して欲しいですね…。なので、

function parseQueryString(url) {
const queryString = url.replace(/.*\?/, '')

const result = {}
if (queryString === url || !queryString) {
return result
}

const urlParams = new URLSearchParams(queryString)
urlParams.forEach((val, key) => {
if (Object.prototype.hasOwnProperty.call(result, key)) {
result[key] = [result[key], val]
} else {
result[key] = val
}
});

return result
}

mock.onGet(/\/api\/hello2?.*/).reply((config) => {
const params = parseQueryString(config.url)
return [200, { config, params }]
})

postされた内容を見る。FormDataの場合。

const formData = new FormData();
formData.append("name", "taro");
formData.append("flag1", "true");
const resp = await $client.post("/api/formPost", formData, {});
mock.onPost("/api/post").reply(config => {
const formData = config.data
const name = formData.get("name")
...
})

PostされたJSONを見る

const resp = await $client.post("/api/postJson", {
name: "hello",
message: "world",
});

config.dataは文字列なのでJSON.parseする。

mock.onPost("/api/postJson").reply((config) => {
const data = JSON.parse(config.data);
return [200, { data }];
});

わざとDelayさせる

delayResponseの全体設定とは別に、個別にDelayさせる。
Promiseを返して、setTimeoutが終わった時にresolveする。

mock.onGet("/api/delayHello").reply((config) => {
const delay = 5000;
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([200, { hello: "world" }]);
}, delay);
});
});

関連記事

参考情報

Vue3でGlobalなFiltersの実現

  • このエントリーをはてなブックマークに追加

Vue3+CompositionAPIを使っていますが、MixinやFilterが無くなったのでその代替をどうするか。

Filterの代わり

globalPropertiesを使います。
globalPropertiesに登録したものは、Templateからそのまま参照できます。

main.js
const app = createApp(App)
app.config.globalProperties.$filters = {
formatNumber(number) {
return Number(number).toLocaleString("en-US", {
maximumFractionDigits: 20
})
}
};
app.mount("#app")
template
{{ $filters.formatNumber(num) }}

setup()からの参照

setup()内では$filtersでは参照できません。参照したい場合は、次のようにgetCurrentInstance()を利用します。

import { defineComponent, getCurrentInstance } from "vue"
export default defineComponent({
setup() {
const app = getCurrentInstance()
const $filters = app.appContext.config.globalProperties.$filters
}
})

sample-code: https://codesandbox.io/s/upbeat-mcnulty-mxi7i?file=/src/App.vue

provide/ inject

もしくは、設計思想的にはprovide, injectを利用するのがよさそうです。

main.js
app.provide("$filters", app.config.globalProperties.$filters)
setup() {
const $filters = inject("$filters")
}

キーは文字列じゃなくて、exportしたSymbolにするのが良さそう。

参考情報

HexoでNodeのバージョンを上げたらWarningが出るようになった

  • このエントリーをはてなブックマークに追加

Hexoが利用しているNodeのバージョンを12.16.1から16.5.0にあげたら次の警告が出るようになった。
Hexoのバージョンは5.4.0です。

(node:6910) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:6910) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
(node:6910) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency
(node:6910) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency
(node:6910) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
(node:6910) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency

--trace-warningsを付けて実行したところ、次の結果が得られた。どうやら、Stylusがよろしくないらしい。

(node:2398) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:42:23)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:57:16)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
(node:2398) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:43:23)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:57:16)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
(node:2398) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:44:25)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:57:16)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
(node:2398) Warning: Accessing non-existent property 'lineno' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:42:23)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:58:17)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
(node:2398) Warning: Accessing non-existent property 'column' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:43:23)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:58:17)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
(node:2398) Warning: Accessing non-existent property 'filename' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:701:11)
at Object.get (node:internal/modules/cjs/loader:715:5)
at Boolean.Node [as constructor] (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/node.js:44:25)
at new Boolean (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/boolean.js:23:8)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/nodes/index.js:58:17)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)
at Module.require (node:internal/modules/cjs/loader:999:19)
at require (node:internal/modules/cjs/helpers:93:18)
at Object.<anonymous> (/mnt/wsl/git-repos/github.com/tamtam180/tamtam180.github.io/blog/node_modules/nib/node_modules/stylus/lib/lexer.js:13:13)
at Module._compile (node:internal/modules/cjs/loader:1095:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:816:12)

依存関係

依存関係を調べます。Hexoのpackage.jsonは次のようになっています。

package.json
{
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server"
},
"hexo": {
"version": "5.4.0"
},
"dependencies": {
"hexo": "^5.4.0",
"hexo-deployer-git": "^3.0.0",
"hexo-generator-archive": "^1.0.0",
"hexo-generator-category": "^1.0.0",
"hexo-generator-feed": "^3.0.0",
"hexo-generator-index": "^2.0.0",
"hexo-generator-search": "^2.4.0",
"hexo-generator-sitemap": "^2.0.0",
"hexo-generator-tag": "^1.0.0",
"hexo-related-popular-posts": "^5.0.1",
"hexo-renderer-ejs": "^1.0.0",
"hexo-renderer-marked": "git+https://github.com/tamtam180/hexo-renderer-marked.git#feature/use-class-table-align",
"hexo-renderer-stylus": "^2.0.1",
"hexo-server": "^2.0.0"
}
}

hexo-renderer-stylusstylus@0.54.8を利用していますが、nibの現時点での最新バージョンである1.1.2では古いStylus0.54.5を見ています。これが良くない。nibのmasterブランチを見ると、このコミット0.54.8を使うようになっていますが、このリビジョンが取り込まれているバージョンはまだリリースされていません。

"hexo-renderer-stylus": "^2.0.1"
"nib": "^1.1.2"
"stylus": "^0.54.5" # これが問題
"stylus": "^0.54.8"

ワークアラウンド

package-lock.jsonで解決されたバージョンを直接弄ってしまい、nibが新しいバージョンのStylusを参照するようにすることで回避できます。

参考情報

nodejsのsemverまとめ

  • このエントリーをはてなブックマークに追加

semverの解釈がエコシステムによって異なったりする場合があるので、nodejs(npm)の場合を備忘録メモ。

3桁の数字で表現:x.y.z

  • 最初の桁はメジャーバージョン
  • 2桁目はマイナーバージョン
  • 3桁目はパッチバージョン

バージョンの上げ方

  • 互換性のないAPIを変更する場合、メジャーバージョンをアップ
  • 下位互換性のある方法で機能を追加する場合、マイナーバージョンをアップ
  • 下位互換性のあるバグ修正を行う場合、パッチバージョンがアップ

バージョン表記の記号

  • ^: 一番左にあるゼロ以外の数値を変更しない

    • ^1.2.3 => 1.2.3 <= version < 2.0.0
    • ^0.13.0 => 0.13.0 <= version < 0.14.0
    • 0.xの場合とそれ以外とでルールが異なるので注意。
  • ~: 一番右のバージョンがあがる

    • ~1.2.3 => 1.2.3 <= version < 1.3.0
    • ~1.2 => 1.2.0 <= version < 1.3.0, 1.2.xと同じ。
    • ~1 => 1 <= version < 2, 1.xと同じ。
  • = ピンポイントでそのバージョン

    • =1.2.3 => 1.2.3
  • - レンジ

    • 1.2.3 - 1.2.4 => 1.2.3 <= version <=1.2.4
    • 両端とも含みます
  • .xという表記もできるようだ

その他

  • 記号なしの場合は、ピンポイントでそのバージョン。(=と同じかな)
  • latest 最新のバージョン。(正確にはTag指定で、npmレポジトリにlatestというタグが存在するから)

複数指定

ルールが分かりづらい場合、分解して書いた方が分かりやすい

  • 1.2.3 - 1.2.5
  • >=1.2.3 <1.3.0
  • <1.0.0 || >2.3.4 || >=3.0.0 <4.0.0

確認する方法

npm semver calculator というツールでオンラインで検証できます。

参考情報

package.jsonでgithubを直接参照する

  • このエントリーをはてなブックマークに追加

通常は、semverを指定していますが、npmのtag, 他にもhttp(s)から始まるURL、GitのURLや、GithubのProject/Repo、Localファイルのパスを指定することができます。

package.json
{
"dependencies": {
"hexo-renderer-ejs": "^1.0.0",
"hexo-renderer-marked": "git+https://github.com/tamtam180/hexo-renderer-marked.git#feature/use-class-table-align"
}
}

npmのtag

"example1": "latest"
"example2": "next"

URL

"example": "http://example.com/"

Git Url

"example1": "git+https://github.com/tamtam180/hexo-renderer-marked.git#feature/use-class-table-align"
"example2": "git+ssh://github.com/tamtam180/hexo-renderer-marked.git#feature/use-class-table-align"

#でブランチやHash指定が可能。

git+ssh://git@github.com:npm/cli.git#v1.0.27
git+ssh://git@github.com:npm/cli#semver:^5.0
git+https://isaacs@github.com/npm/cli.git
git://github.com/npm/cli.git#v1.0.27

Github Url

"example1": "tamtam180/hexo-renderer-marked.git#feature\/use-class-table-align"

ブランチのスラッシュはエスケープが必要?

Localファイル

サンプル
../foo/bar
~/foo/bar
./foo/bar
/foo/bar

参考情報

ESLintのno-unused-varsを特定の箇所だけ無効にしたい

  • このエントリーをはてなブックマークに追加

例えば次のようなクラスを定義したい場合、processcontextが未使用なのでESLintにno-unused-varsの警告を受けます。このルールを部分的に無効にする方法にはいくつかあります。

Base
class Base {
constructor() {
}
execute() {
const context = {}
this.process(context)
}
// eslint-disable-next-line no-unused-vars
process(context) {
// override
}
}

特定の次の行を無効にする

eslint-disable-next-line

// eslint-disable-next-line no-unused-vars
const myVars = 1;

同じ行を無効にする

eslint-disable-line

const myVars = 1; // eslint-disable-line no-unused-vars

範囲を無効にする

/* eslint-disable no-unused-vars */
const myVars1 = 1;
const myVars2 = 1;
const myVars3 = 1;
/* eslint-enable no-unused-vars */

ファイル全体を対象に無効にする

ファイルの先頭に次のブロックコメントを記載します。

/* eslint no-unused-vars: 0 */

注意点として、/**/のコメント記法を利用することです。
また、指定している数字は、0=”off”, 1=”warn”, 2=”error”の意味です。

参考

ファイルをドロップする要素が子要素を持っているとDragLeaveが発生してしまう

  • このエントリーをはてなブックマークに追加

Drag and Dropを実装する時に、ドロップする要素が子要素を持っていると、DragLeaveが発生してしまう。
子要素(別要素)にEnterしたということは、対象の要素がLeaveしたということになるらしいです。

対策の方法は色々とありますが、カウンタを用意するのが一番簡単です。
ひとまず、Vue3+CompositionAPIでサンプルを。

template
<div :class="{enter: ddCounter>0}"
@dragenter="fileDragEnter"
@dragleave="fileDragLeave"
@dragover.prevent
@drop.prevent="fileDrop"
>
</div>
script
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup() {
const ddCounter = ref(0)
const fileDragEnter = () => { ddCounter.value++ }
const fileDragLeave = () => { ddCounter.value-- }
const fileDrop = (event) => {
ddCounter.value = 0
}
return {
ddCounter,
fileDragEnter,
fileDragLeave,
fileDrop,
}
}
})
style
.enter {
border: 5px dotted powderblue;
}

サンプルコード ▶ Vue SFC Playground

参考

MySQLのJSONからboolean型のGenerated Columnを定義する時の注意点

  • このエントリーをはてなブックマークに追加

確認したバージョンは、MySQL8.0.25。
JSONのtrue/falseを参照してMySQLのboolean(tinyint)にする時、ついうっかりvarchar等と同じ感覚でjson_unquoteをするとエラーになる。

テスト用の定義

create table test1 (
id bigint not null,
data json not null,
name varchar(32) as (json_unquote(data->'$.name')) stored,
flag1 boolean as (json_unquote(data->'$.flag1')) stored, -- ※間違い
flag2 boolean as ((data->'$.flag2')) stored,
constraint pk_test1 primary key(id)
);

間違い

insert into test1 set id = 1, data = '{"name":"hello", "flag1": true, "flag2": true}';

flag1列でエラー。boolean列にjson_unquoteしているから。

ERROR 1366 (HY000): Incorrect integer value: 'true' for column 'flag1' at row 1

正しい

flag2はjson_unquoteをしていないので成功する。

insert into test1 set id = 1, data = '{"name":"hello", "flag2": true}';
insert into test1 set id = 2, data = '{"name":"hello", "flag2": false}';
select * from test1;
+----+-----------------------------------+-------+-------+-------+
| id | data | name | flag1 | flag2 |
+----+-----------------------------------+-------+-------+-------+
| 1 | {"name": "hello", "flag2": true} | hello | NULL | 1 |
| 2 | {"name": "hello", "flag2": false} | hello | NULL | 0 |
+----+-----------------------------------+-------+-------+-------+

型は何になっている??

MySQLにはpg_typeofみたいなものはないので、Selectの結果でTEMPテーブルを作って確認します。

create temporary table tmp1
select
(json_extract('{"name":"hello", "flag1": true}', '$.flag1')) as c1,
(json_unquote(json_extract('{"name":"hello", "flag1": true}', '$.flag1'))) as c2
;

desc tmp1;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| c1 | json | YES | | NULL | NULL |
| c2 | longtext | YES | | NULL | NULL |
+-------+----------+------+-----+---------+-------+

select * from tmp1;
+------+------+
| c1 | c2 |
+------+------+
| true | true |
+------+------+

json_unquoteしたものはlongtextのようです。
つまり、jsonからtinyintへの変換は成功するが、longtextからtinyintへの変換は失敗するということになります。

確認してみます。

select cast(json_extract('{"name":"hello", "flag1": true}', '$.flag1') as signed) as x;
+------+
| x |
+------+
| 1 |
+------+
1 row in set (0.00 sec)

対して、

select cast(json_unquote(json_extract('{"name":"hello", "flag1": false}', '$.flag1')) as signed) as x;
+------+
| x |
+------+
| 0 |
+------+
1 row in set, 1 warning (0.00 sec)

駄目な上に警告がでています。

 show warnings;
+---------+------+--------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------+
| Warning | 1292 | Truncated incorrect INTEGER value: 'false' |
+---------+------+--------------------------------------------+
1 row in set (0.00 sec)

検証の単純化

-- OK
select cast(true as signed);
select cast(cast('true' as json) as signed);

-- NG
select cast('true' as signed)

そんなわけで、JSONのbooleanを扱うときは、気をつけましょうということで。

MySQL8でOut of sort memoryが発生するようになった

  • このエントリーをはてなブックマークに追加

MySQL8.0.21から8.0.25にアップグレードした後、次のような単純なクエリを発行したらERROR 1038 (HY001): Out of sort memory, consider increasing server sort buffer sizeが発生するようになった。

SELECT *
FROM hoge_table
ORDER BY created_at DESC LIMIT 10

このテーブルは次のように、JSONカラムとそれを参照する大量のGeneratedカラムを持つテーブルです。

create table hoge_table(
id varchar(32) ascii not null,
data json not null,
c1 varchar(256) as (json_unquote(data->'$.c1')) stored,
c2 varchar(256) as (json_unquote(data->'$.c2')) stored,
c3 varchar(256) as (json_unquote(data->'$.c3')) stored,
.... 20カラムくらい,
constraint pk_hoge_table primary key (id)
) default charset utf8mb4 row_format=compressed key_block_size=4;

Bug Ticket:103225に情報があります。

対策

sort_buffer_sizeが256KBなので1Mに増やしました。innodb_sort_buffer_sizeではなくsort_buffer_sizeです。私は幸いにして、これで回避できました。

オンラインで変更

set global sort_buffer_size = 1*1024*1024;
# 変わらない場合は..
set @@sort_buffer_size = 1*1024*1024;

show variables like '%sort%';
/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
sort_buffer_size = 1M

参考

RubyのDatadog ClientからMetricsが記録されない場合がある

  • このエントリーをはてなブックマークに追加

dogstatsd-rubyを利用しています。
バージョン5から、Client側が内部バッファを持つようになった影響で、終了時には明示的にflushしないとバッファの内容が残ったままプログラムが終了し、Metricsが喪失します。

auto-closeの処理が入っていると思いきや、一瞬だけ入ったのですが、すぐにrevertされました。

This feature is introducing way too much complexity for applications using forks: since the auto-close feature use a global list to store the instances list, it could lead to a fork dying trying to close an instance which has been created before the fork. It's not wrong, but it's complexifying the thinking process. It is better and important to manually close instances when they are not needed anymore (they create a socket and a thread, not closing the instance would leak these resources).

プログラムをForkする場合に、すごく複雑になるから。だそうです。

そんなわけで、自前で簡単に処理するように。

require "datadog/statsd"
datadog = Datadog::Statsd.new()
at_exit {
datadog.flush(sync: true) if datadog
}

v4と同じ挙動である、クライアントでバッファリングしないようにする事も可能です。その場合は明示的にflushする必要はありません。(大量にMetricsを送る場合は非推奨)

require "datadog/statsd"
datadog = Datadog::Statsd.new(single_thread: true, buffer_max_pool_size: 1)

参考