├── docs ├── .hugo_build.lock ├── themes │ └── hugo-geekdoc │ │ ├── VERSION │ │ ├── static │ │ ├── custom.css │ │ ├── favicon │ │ │ ├── favicon.ico │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon-48x48.png │ │ │ ├── mstile-144x144.png │ │ │ ├── mstile-150x150.png │ │ │ ├── mstile-310x150.png │ │ │ ├── mstile-310x310.png │ │ │ ├── mstile-70x70.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-36x36.png │ │ │ ├── android-chrome-48x48.png │ │ │ ├── android-chrome-72x72.png │ │ │ ├── android-chrome-96x96.png │ │ │ ├── android-chrome-144x144.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-256x256.png │ │ │ ├── android-chrome-384x384.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon-114x114.png │ │ │ ├── apple-touch-icon-120x120.png │ │ │ ├── apple-touch-icon-144x144.png │ │ │ ├── apple-touch-icon-152x152.png │ │ │ ├── apple-touch-icon-167x167.png │ │ │ ├── apple-touch-icon-180x180.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-60x60.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ ├── apple-touch-icon-76x76.png │ │ │ ├── apple-touch-icon-1024x1024.png │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ ├── apple-touch-startup-image-1136x640.png │ │ │ ├── apple-touch-startup-image-1334x750.png │ │ │ ├── apple-touch-startup-image-1792x828.png │ │ │ ├── apple-touch-startup-image-640x1136.png │ │ │ ├── apple-touch-startup-image-750x1334.png │ │ │ ├── apple-touch-startup-image-828x1792.png │ │ │ ├── apple-touch-startup-image-1125x2436.png │ │ │ ├── apple-touch-startup-image-1170x2532.png │ │ │ ├── apple-touch-startup-image-1242x2208.png │ │ │ ├── apple-touch-startup-image-1242x2688.png │ │ │ ├── apple-touch-startup-image-1284x2778.png │ │ │ ├── apple-touch-startup-image-1536x2048.png │ │ │ ├── apple-touch-startup-image-1620x2160.png │ │ │ ├── apple-touch-startup-image-1668x2224.png │ │ │ ├── apple-touch-startup-image-1668x2388.png │ │ │ ├── apple-touch-startup-image-2048x1536.png │ │ │ ├── apple-touch-startup-image-2048x2732.png │ │ │ ├── apple-touch-startup-image-2160x1620.png │ │ │ ├── apple-touch-startup-image-2208x1242.png │ │ │ ├── apple-touch-startup-image-2224x1668.png │ │ │ ├── apple-touch-startup-image-2388x1668.png │ │ │ ├── apple-touch-startup-image-2436x1125.png │ │ │ ├── apple-touch-startup-image-2532x1170.png │ │ │ ├── apple-touch-startup-image-2688x1242.png │ │ │ ├── apple-touch-startup-image-2732x2048.png │ │ │ ├── apple-touch-startup-image-2778x1284.png │ │ │ ├── browserconfig.xml │ │ │ ├── manifest.json │ │ │ └── favicon.svg │ │ ├── fonts │ │ │ ├── Metropolis.woff │ │ │ ├── GeekdocIcons.woff │ │ │ ├── Metropolis.woff2 │ │ │ ├── GeekdocIcons.woff2 │ │ │ ├── KaTeX_Main-Bold.woff │ │ │ ├── LiberationMono.woff │ │ │ ├── LiberationMono.woff2 │ │ │ ├── LiberationSans.woff │ │ │ ├── LiberationSans.woff2 │ │ │ ├── KaTeX_AMS-Regular.woff │ │ │ ├── KaTeX_Main-Bold.woff2 │ │ │ ├── KaTeX_Main-Italic.woff │ │ │ ├── KaTeX_Math-Italic.woff │ │ │ ├── KaTeX_AMS-Regular.woff2 │ │ │ ├── KaTeX_Fraktur-Bold.woff │ │ │ ├── KaTeX_Fraktur-Bold.woff2 │ │ │ ├── KaTeX_Main-Italic.woff2 │ │ │ ├── KaTeX_Main-Regular.woff │ │ │ ├── KaTeX_Main-Regular.woff2 │ │ │ ├── KaTeX_Math-Italic.woff2 │ │ │ ├── KaTeX_SansSerif-Bold.woff │ │ │ ├── KaTeX_Script-Regular.woff │ │ │ ├── KaTeX_Size1-Regular.woff │ │ │ ├── KaTeX_Size1-Regular.woff2 │ │ │ ├── KaTeX_Size2-Regular.woff │ │ │ ├── KaTeX_Size2-Regular.woff2 │ │ │ ├── KaTeX_Size3-Regular.woff │ │ │ ├── KaTeX_Size3-Regular.woff2 │ │ │ ├── KaTeX_Size4-Regular.woff │ │ │ ├── KaTeX_Size4-Regular.woff2 │ │ │ ├── LiberationSans-Bold.woff │ │ │ ├── LiberationSans-Bold.woff2 │ │ │ ├── KaTeX_Caligraphic-Bold.woff │ │ │ ├── KaTeX_Fraktur-Regular.woff │ │ │ ├── KaTeX_Fraktur-Regular.woff2 │ │ │ ├── KaTeX_Main-BoldItalic.woff │ │ │ ├── KaTeX_Main-BoldItalic.woff2 │ │ │ ├── KaTeX_Math-BoldItalic.woff │ │ │ ├── KaTeX_Math-BoldItalic.woff2 │ │ │ ├── KaTeX_SansSerif-Bold.woff2 │ │ │ ├── KaTeX_SansSerif-Italic.woff │ │ │ ├── KaTeX_Script-Regular.woff2 │ │ │ ├── LiberationSans-Italic.woff │ │ │ ├── LiberationSans-Italic.woff2 │ │ │ ├── KaTeX_Caligraphic-Bold.woff2 │ │ │ ├── KaTeX_Caligraphic-Regular.woff │ │ │ ├── KaTeX_SansSerif-Italic.woff2 │ │ │ ├── KaTeX_SansSerif-Regular.woff │ │ │ ├── KaTeX_SansSerif-Regular.woff2 │ │ │ ├── KaTeX_Typewriter-Regular.woff │ │ │ ├── KaTeX_Typewriter-Regular.woff2 │ │ │ ├── LiberationSans-BoldItalic.woff │ │ │ ├── KaTeX_Caligraphic-Regular.woff2 │ │ │ └── LiberationSans-BoldItalic.woff2 │ │ ├── js │ │ │ ├── main-fbb9c8b0.bundle.min.js.LICENSE.txt │ │ │ ├── search-83320133.bundle.min.js.LICENSE.txt │ │ │ ├── 484-21d9ea41.chunk.min.js.LICENSE.txt │ │ │ └── 926-40cc1be5.chunk.min.js.LICENSE.txt │ │ ├── print-735ccc12.min.css │ │ ├── mobile-79ddc617.min.css │ │ └── brand.svg │ │ ├── images │ │ ├── tn.png │ │ ├── readme.png │ │ └── screenshot.png │ │ ├── layouts │ │ ├── partials │ │ │ ├── head │ │ │ │ ├── custom.html │ │ │ │ ├── rel-me.html │ │ │ │ ├── microformats.html │ │ │ │ ├── favicons.html │ │ │ │ ├── meta.html │ │ │ │ └── others.html │ │ │ ├── svg-icon-symbols.html │ │ │ ├── utils │ │ │ │ ├── title.html │ │ │ │ ├── content.html │ │ │ │ ├── featured.html │ │ │ │ └── description.html │ │ │ ├── foot.html │ │ │ ├── microformats │ │ │ │ ├── twitter_cards.html │ │ │ │ ├── opengraph.html │ │ │ │ └── schema.html │ │ │ ├── search.html │ │ │ ├── pagination.html │ │ │ ├── menu-extra.html │ │ │ ├── menu.html │ │ │ ├── posts │ │ │ │ └── metadata.html │ │ │ ├── language.html │ │ │ ├── site-footer.html │ │ │ ├── menu-nextprev.html │ │ │ ├── page-header.html │ │ │ └── site-header.html │ │ ├── robots.txt │ │ ├── shortcodes │ │ │ ├── icon.html │ │ │ ├── tab.html │ │ │ ├── mermaid.html │ │ │ ├── toc.html │ │ │ ├── columns.html │ │ │ ├── expand.html │ │ │ ├── hint.html │ │ │ ├── include.html │ │ │ ├── tabs.html │ │ │ ├── katex.html │ │ │ ├── button.html │ │ │ ├── progress.html │ │ │ ├── toc-tree.html │ │ │ ├── propertylist.html │ │ │ └── img.html │ │ ├── _default │ │ │ ├── _markup │ │ │ │ ├── render-image.html │ │ │ │ ├── render-codeblock-mermaid.html │ │ │ │ ├── render-link.html │ │ │ │ └── render-heading.html │ │ │ ├── list.html │ │ │ ├── single.html │ │ │ ├── terms.html │ │ │ ├── taxonomy.html │ │ │ └── baseof.html │ │ ├── posts │ │ │ ├── single.html │ │ │ └── list.html │ │ └── 404.html │ │ ├── archetypes │ │ ├── posts.md │ │ └── docs.md │ │ ├── theme.toml │ │ ├── assets │ │ └── search │ │ │ ├── config.json │ │ │ └── data.json │ │ ├── LICENSE │ │ └── i18n │ │ ├── zh-cn.yaml │ │ ├── ja.yaml │ │ ├── en.yaml │ │ ├── nl.yaml │ │ ├── de.yaml │ │ ├── cs.yaml │ │ └── it.yaml ├── content │ ├── api │ │ ├── _index.md │ │ ├── stats.md │ │ └── data.md │ ├── data │ │ └── _index.md │ ├── intro │ │ ├── _index.md │ │ └── tldr.md │ ├── benchmark │ │ ├── _index.md │ │ └── error-handling.md │ ├── operate │ │ ├── _index.md │ │ └── client-server.md │ ├── syntax │ │ ├── _index.md │ │ ├── values.md │ │ └── params.md │ └── _index.md ├── serve.sh ├── archetypes │ └── default.md ├── static │ └── custom.css └── config.toml ├── test ├── config │ └── b1 │ │ ├── trx.sql │ │ ├── _all.yaml │ │ └── stage.yaml ├── run │ ├── select-1 │ │ ├── test.sql │ │ └── test.yaml │ ├── scope │ │ ├── 002.sql │ │ ├── 001.sql │ │ ├── workload_cg_alloc.yaml │ │ ├── statement.yaml │ │ ├── client.yaml │ │ ├── iter.yaml │ │ └── trx.yaml │ └── columns │ │ ├── run.sql │ │ ├── test.yaml │ │ └── setup.sql ├── trx │ ├── 001.sql │ ├── 002.sql │ ├── copy-no.sql │ ├── copy3-1.sql │ ├── copy3-2.sql │ ├── 003.sql │ └── rowscope-csv.sql ├── coverage ├── docker │ └── docker-compose.yaml ├── mock │ ├── stats_reporter.go │ └── data_generator.go └── mysql.go ├── benchmarks ├── intro │ ├── read-only.sql │ ├── insert-rows.sql │ ├── rw-trx.sql │ ├── schema.sql │ ├── read-only.yaml │ ├── row-lock.yaml │ └── setup.yaml ├── jcole-bug-97001 │ ├── shift-rows.sql │ ├── get-1-row.sql │ ├── insert-rows.sql │ ├── schema.sql │ ├── before.yaml │ ├── after.yaml │ └── setup.yaml ├── aurora │ ├── trx │ │ ├── analyze-tables.sql │ │ ├── insert-rows.sql │ │ ├── write-only.sql │ │ └── create-tables.sql │ ├── _all.yaml │ ├── setup.yaml │ └── write-only.yaml ├── sysbench │ ├── trx │ │ ├── secondary-index.sql │ │ ├── insert-rows.sql │ │ ├── write-only.sql │ │ ├── schema.sql │ │ └── read-only.sql │ ├── read-only.yaml │ ├── setup.yaml │ ├── write-only.yaml │ └── _all.yaml └── xfer │ ├── trx │ ├── insert-xfers.sql │ ├── insert-cust-bal.sql │ ├── xfer.sql │ └── schema.sql │ ├── _all.yaml │ ├── setup.yaml │ └── xfer.yaml ├── README.md ├── .gitignore ├── bin └── finch │ └── main.go ├── aws └── rds_test.go ├── go.mod ├── limit ├── data_test.go └── rate.go ├── data ├── string_test.go ├── string.go ├── column.go ├── id_test.go └── id.go ├── compute └── server_test.go ├── stage └── stage_test.go ├── dbconn ├── factory_test.go └── mycnf.go ├── stats ├── reporter_test.go └── remote.go ├── boot └── cmdline.go ├── finch_test.go ├── .github └── workflows │ └── hugo.yml ├── config └── config_test.go └── proto └── proto.go /docs/.hugo_build.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/config/b1/trx.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 1 3 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/VERSION: -------------------------------------------------------------------------------- 1 | v0.39.1 2 | -------------------------------------------------------------------------------- /test/run/select-1/test.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT 1 3 | -------------------------------------------------------------------------------- /test/trx/001.sql: -------------------------------------------------------------------------------- 1 | select c from t where id=@id 2 | -------------------------------------------------------------------------------- /docs/content/api/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 6 3 | --- 4 | -------------------------------------------------------------------------------- /docs/content/data/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 4 3 | --- 4 | -------------------------------------------------------------------------------- /docs/content/intro/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 1 3 | --- 4 | -------------------------------------------------------------------------------- /test/config/b1/_all.yaml: -------------------------------------------------------------------------------- 1 | 2 | params: 3 | foo: bar 4 | -------------------------------------------------------------------------------- /docs/content/benchmark/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 2 3 | --- 4 | -------------------------------------------------------------------------------- /docs/content/operate/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 5 3 | --- 4 | -------------------------------------------------------------------------------- /docs/content/syntax/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 3 3 | --- 4 | -------------------------------------------------------------------------------- /test/trx/002.sql: -------------------------------------------------------------------------------- 1 | 2 | SELECT c FROM t WHERE id BETWEEN @d AND @PREV 3 | -------------------------------------------------------------------------------- /benchmarks/intro/read-only.sql: -------------------------------------------------------------------------------- 1 | SELECT n, c FROM finch.t1 WHERE id = @id 2 | -------------------------------------------------------------------------------- /docs/serve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | hugo serve --baseURL="http://127.1:1313/finch" 3 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/custom.css: -------------------------------------------------------------------------------- 1 | /* You can add custom styles here. */ 2 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/shift-rows.sql: -------------------------------------------------------------------------------- 1 | UPDATE t SET id=id+1000000 WHERE other_id = 555 2 | -------------------------------------------------------------------------------- /test/trx/copy-no.sql: -------------------------------------------------------------------------------- 1 | 2 | -- copies: 2 3 | select c from t/*!copy-number*/ where id=1 4 | -------------------------------------------------------------------------------- /test/trx/copy3-1.sql: -------------------------------------------------------------------------------- 1 | 2 | -- copies: 3 3 | -- prepare 4 | select c from t where id=@id 5 | -------------------------------------------------------------------------------- /test/trx/copy3-2.sql: -------------------------------------------------------------------------------- 1 | 2 | -- prepare 3 | -- copies: 3 4 | select c from t where id=@id 5 | -------------------------------------------------------------------------------- /test/run/select-1/test.yaml: -------------------------------------------------------------------------------- 1 | 2 | stage: 3 | runtime: 1s 4 | trx: 5 | - file: test.sql 6 | -------------------------------------------------------------------------------- /test/coverage: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go test -coverprofile=coverage.out "$@" 3 | go tool cover -html=coverage.out 4 | -------------------------------------------------------------------------------- /benchmarks/aurora/trx/analyze-tables.sql: -------------------------------------------------------------------------------- 1 | -- copies: $params.tables 2 | ANALYZE TABLE sbtest/*!copy-number*/ 3 | -------------------------------------------------------------------------------- /benchmarks/intro/insert-rows.sql: -------------------------------------------------------------------------------- 1 | -- rows: 100,000 2 | INSERT INTO finch.t1 (id, n, c) VALUES (@id, @n, @c) 3 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/get-1-row.sql: -------------------------------------------------------------------------------- 1 | SELECT non_covered_column FROM t WHERE other_id = 555 ORDER BY id ASC LIMIT 1 2 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/images/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/images/tn.png -------------------------------------------------------------------------------- /test/trx/003.sql: -------------------------------------------------------------------------------- 1 | 2 | 3 | -- save-columns: @c 4 | select c from t1 where id=1 5 | 6 | 7 | insert into t2 values (@c) 8 | -------------------------------------------------------------------------------- /benchmarks/sysbench/trx/secondary-index.sql: -------------------------------------------------------------------------------- 1 | 2 | ALTER TABLE sbtest1 ADD INDEX `k_1` (`k`) 3 | 4 | ANALYZE TABLE sbtest1 5 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/custom.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/archetypes/posts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | --- 5 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/images/readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/images/readme.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /tags/* 3 | 4 | Sitemap: {{ "sitemap.xml" | absURL }} 5 | -------------------------------------------------------------------------------- /test/config/b1/stage.yaml: -------------------------------------------------------------------------------- 1 | 2 | stage: 3 | name: "test" 4 | params: 5 | foo: "test" 6 | trx: 7 | - file: trx.sql 8 | -------------------------------------------------------------------------------- /docs/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ replace .Name "-" " " | title }}" 3 | date: {{ .Date }} 4 | draft: true 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/images/screenshot.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/rel-me.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MySQL Benchmarking 2 | 3 | Finch is a MySQL benchmarking tool. 4 | [Read the docs](https://block.github.io/finch/) to learn more. 5 | -------------------------------------------------------------------------------- /test/run/scope/002.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO finch.scopetest (a, r, eg, cg, c, iter, trx, s) VALUES (@a, @r, @clientid, @PREV, @PREV, @PREV, @PREV, @PREV) 2 | -------------------------------------------------------------------------------- /test/trx/rowscope-csv.sql: -------------------------------------------------------------------------------- 1 | SELECT 1 -- /*!csv 2 (@d, @d %% 1000, '@d', '@d')*/ 2 | 3 | SELECT 1 -- /*!csv 2 (@d(), @d %% 1000, '@d', '@d')*/ 4 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/favicon.ico -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/Metropolis.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/Metropolis.woff -------------------------------------------------------------------------------- /benchmarks/sysbench/trx/insert-rows.sql: -------------------------------------------------------------------------------- 1 | 2 | -- prepare 3 | -- rows: ${params.rows} 4 | INSERT INTO sbtest1 VALUES /*!csv 1000 (NULL, @k, @c, @pad) */ 5 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/GeekdocIcons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/GeekdocIcons.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/Metropolis.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/Metropolis.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/favicon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/favicon-48x48.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/GeekdocIcons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/GeekdocIcons.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Bold.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationMono.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationMono.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationMono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationMono.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans.woff2 -------------------------------------------------------------------------------- /benchmarks/intro/rw-trx.sql: -------------------------------------------------------------------------------- 1 | BEGIN 2 | 3 | SELECT c FROM finch.t1 WHERE id = @id FOR UPDATE 4 | 5 | UPDATE finch.t1 SET n = n + 1 WHERE id = @id 6 | 7 | COMMIT 8 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_AMS-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_AMS-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Bold.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Italic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-Italic.woff -------------------------------------------------------------------------------- /benchmarks/intro/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE t1 ( 2 | id INT UNSIGNED NOT NULL PRIMARY KEY, 3 | n INT NOT NULL, 4 | c VARCHAR(100) NOT NULL, 5 | INDEX (n) 6 | ) 7 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/insert-rows.sql: -------------------------------------------------------------------------------- 1 | -- prepare 2 | INSERT INTO t (id, other_id, covered_column, non_covered_column) VALUES /*!csv 5000 (@n(), @n % 1000, @n, @n)*/ 3 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_AMS-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_AMS-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Bold.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Bold.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Italic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-Italic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Bold.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Script-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Script-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size1-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size1-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size1-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size1-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size2-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size2-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size2-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size2-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size3-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size3-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size3-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size3-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size4-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size4-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size4-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Size4-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Bold.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Bold.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-36x36.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-48x48.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-72x72.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-96x96.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Bold.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Fraktur-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-BoldItalic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Main-BoldItalic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-BoldItalic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Math-BoldItalic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Bold.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Italic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Script-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Script-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Italic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-Italic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-144x144.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-256x256.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-384x384.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-167x167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-167x167.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Bold.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Italic.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_SansSerif-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Typewriter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Typewriter-Regular.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Typewriter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Typewriter-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-BoldItalic.woff -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-1024x1024.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/KaTeX_Caligraphic-Regular.woff2 -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/fonts/LiberationSans-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/fonts/LiberationSans-BoldItalic.woff2 -------------------------------------------------------------------------------- /benchmarks/aurora/trx/insert-rows.sql: -------------------------------------------------------------------------------- 1 | -- prepare 2 | -- copies: $params.tables 3 | -- rows: $params.rows 4 | INSERT INTO sbtest/*!copy-number*/ VALUES /*!csv 1000 (NULL, @k, @c, @pad)*/ 5 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/js/main-fbb9c8b0.bundle.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.11 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | -------------------------------------------------------------------------------- /benchmarks/aurora/_all.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#aurora 2 | 3 | params: 4 | tables: 250 5 | rows: 25000 6 | instances: 4 7 | clients: 1000 # per instance 8 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/archetypes/docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "{{ .Name | humanize | title }}" 3 | weight: 1 4 | # geekdocFlatSection: false 5 | # geekdocToc: 6 6 | # geekdocHidden: false 7 | --- 8 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/icon.html: -------------------------------------------------------------------------------- 1 | {{ $id := .Get 0 }} 2 | 3 | {{- with $id -}} 4 | 5 | {{- end -}} 6 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1136x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1136x640.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1334x750.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1334x750.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1792x828.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1792x828.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-640x1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-640x1136.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-750x1334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-750x1334.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-828x1792.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-828x1792.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1125x2436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1125x2436.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1170x2532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1170x2532.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1242x2208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1242x2208.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1242x2688.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1242x2688.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1284x2778.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1284x2778.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1536x2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1536x2048.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1620x2160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1620x2160.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1668x2224.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1668x2224.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1668x2388.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-1668x2388.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2048x1536.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2048x1536.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2048x2732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2048x2732.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2160x1620.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2160x1620.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2208x1242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2208x1242.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2224x1668.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2224x1668.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2388x1668.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2388x1668.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2436x1125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2436x1125.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2532x1170.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2532x1170.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2688x1242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2688x1242.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2732x2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2732x2048.png -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2778x1284.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/block/finch/HEAD/docs/themes/hugo-geekdoc/static/favicon/apple-touch-startup-image-2778x1284.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tmp 2 | *.swp 3 | *.out 4 | *.prof 5 | bin/finch/finch 6 | bin/finch/finch-amd64 7 | bin/finch/*.yaml 8 | bin/finch/*.sql 9 | bin/finch/*.sh 10 | docs/.hugo_build.lock 11 | docs/public/ 12 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/microformats.html: -------------------------------------------------------------------------------- 1 | {{ partial "microformats/opengraph.html" . }} 2 | {{ partial "microformats/twitter_cards.html" . }} 3 | {{ partial "microformats/schema" . }} 4 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/svg-icon-symbols.html: -------------------------------------------------------------------------------- 1 | {{ range resources.Match "sprites/*.svg" }} 2 | {{ printf "" . | safeHTML }} 3 | {{ .Content | safeHTML }} 4 | {{ end }} 5 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/_markup/render-image.html: -------------------------------------------------------------------------------- 1 | {{ .Text }} 6 | {{- /* Drop trailing newlines */ -}} 7 | -------------------------------------------------------------------------------- /benchmarks/xfer/trx/insert-xfers.sql: -------------------------------------------------------------------------------- 1 | 2 | -- prepare 3 | -- table-size: xfers $params.xfers-size 4 | INSERT INTO xfers VALUES /*!csv 1000 (NULL, @x_token, 0, 'USD', @s_token, @r_token, 1, @c1, @c2, @c3, NULL, NULL, NULL, 0, 0, NOW(), NOW()) */ 5 | -------------------------------------------------------------------------------- /bin/finch/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | 8 | "github.com/square/finch/boot" 9 | ) 10 | 11 | func main() { 12 | if err := boot.Up(boot.Env{}); err != nil { 13 | log.Fatal(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /benchmarks/intro/read-only.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#intro 2 | 3 | stage: 4 | runtime: 10s 5 | workload: 6 | - clients: 4 7 | trx: 8 | - file: read-only.sql 9 | data: 10 | id: 11 | generator: "int" 12 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/js/search-83320133.bundle.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /**! 2 | * FlexSearch.js v0.7.31 (Compact) 3 | * Copyright 2018-2022 Nextapps GmbH 4 | * Author: Thomas Wilkerling 5 | * Licence: Apache-2.0 6 | * https://github.com/nextapps-de/flexsearch 7 | */ 8 | -------------------------------------------------------------------------------- /test/run/scope/001.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO finch.scopetest (a, r, eg, cg, c, iter, trx, s) VALUES (@a, @r, @clientid, @PREV, @PREV, @PREV, @PREV, @PREV) 2 | 3 | INSERT INTO finch.scopetest (a, r, eg, cg, c, iter, trx, s) VALUES (@a, @r, @clientid, @PREV, @PREV, @PREV, @PREV, @PREV) 4 | -------------------------------------------------------------------------------- /benchmarks/sysbench/trx/write-only.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN 3 | 4 | UPDATE sbtest1 SET k=k+1 WHERE id=@id 5 | 6 | UPDATE sbtest1 SET c=@c WHERE id=@id 7 | 8 | DELETE FROM sbtest1 WHERE id=@del_id 9 | 10 | INSERT INTO sbtest1 (id, k, c, pad) VALUES (@del_id, @k, @c, @pad) 11 | 12 | COMMIT 13 | -------------------------------------------------------------------------------- /test/run/columns/run.sql: -------------------------------------------------------------------------------- 1 | 2 | -- save-insert-id: @a 3 | INSERT INTO coltest1 (a) VALUES (NULL) 4 | 5 | INSERT INTO coltest2 (x, y) VALUES (@a, "0x75") 6 | 7 | -- save-columns: @x, @y, _ 8 | SELECT x, y, z FROM coltest2 LIMIT 1 9 | 10 | INSERT INTO coltest3 (x, y) VALUES (@x, @y) 11 | -------------------------------------------------------------------------------- /benchmarks/aurora/trx/write-only.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN 3 | 4 | UPDATE sbtest@tableNo SET k=k+1 WHERE id=@id 5 | 6 | UPDATE sbtest@tableNo SET c=@c WHERE id=@id 7 | 8 | DELETE FROM sbtest@tableNo WHERE id=@del_id 9 | 10 | INSERT INTO sbtest@tableNo (id, k, c, pad) VALUES (@del_id, @k, @c, @pad) 11 | 12 | COMMIT 13 | -------------------------------------------------------------------------------- /benchmarks/sysbench/trx/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE sbtest1 ( 3 | id int NOT NULL AUTO_INCREMENT, 4 | k int NOT NULL DEFAULT '0', 5 | c char(120) NOT NULL DEFAULT '', 6 | pad char(60) NOT NULL DEFAULT '', 7 | PRIMARY KEY (id) 8 | /* Secondary index added after loading rows */ 9 | ) ENGINE=InnoDB 10 | -------------------------------------------------------------------------------- /test/run/columns/test.yaml: -------------------------------------------------------------------------------- 1 | 2 | stage: 3 | stats: 4 | disable: true 5 | workload: 6 | - trx: [setup.sql, run.sql] 7 | iter: 1 8 | trx: 9 | - file: setup.sql 10 | - file: run.sql 11 | data: 12 | y: 13 | generator: column 14 | params: 15 | quote-value: yes 16 | -------------------------------------------------------------------------------- /benchmarks/aurora/trx/create-tables.sql: -------------------------------------------------------------------------------- 1 | -- copies: $params.tables 2 | CREATE TABLE sbtest/*!copy-number*/ ( 3 | `id` int NOT NULL AUTO_INCREMENT, 4 | `k` int NOT NULL DEFAULT '0', 5 | `c` char(120) NOT NULL DEFAULT '', 6 | `pad` char(60) NOT NULL DEFAULT '', 7 | PRIMARY KEY (`id`), 8 | KEY `k_1` (`k`) 9 | ) ENGINE=InnoDB; 10 | -------------------------------------------------------------------------------- /benchmarks/intro/row-lock.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#intro 2 | 3 | stage: 4 | runtime: 20s 5 | workload: 6 | - clients: 4 7 | trx: 8 | - file: rw-trx.sql 9 | data: 10 | id: 11 | generator: "int" 12 | scope: trx 13 | params: 14 | max: 1,000 15 | -------------------------------------------------------------------------------- /benchmarks/intro/setup.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#intro 2 | 3 | stage: 4 | trx: 5 | - file: schema.sql 6 | - file: insert-rows.sql 7 | data: 8 | id: 9 | generator: "auto-inc" 10 | n: 11 | generator: "int" 12 | c: 13 | generator: "str-fill-az" 14 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE t ( 2 | id BIGINT NOT NULL, 3 | other_id BIGINT NOT NULL, 4 | covered_column VARCHAR(50) NOT NULL, 5 | non_covered_column VARCHAR(50) NOT NULL, 6 | PRIMARY KEY (id), 7 | INDEX index_other_id_covered_column (other_id, covered_column) 8 | ) 9 | -------------------------------------------------------------------------------- /docs/content/api/stats.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | ```go 5 | type Reporter interface { 6 | Report(from []Instance) 7 | Stop() 8 | } 9 | 10 | type ReporterFactory interface { 11 | Make(name string, opts map[string]string) (Reporter, error) 12 | } 13 | ``` 14 | 15 | Implement and register by calling `stats.Register(name string, f ReporterFactory) error`. 16 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/list.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ partial "page-header" . }} 3 | 4 | 5 |
8 |

{{ partial "utils/title" . }}

9 | {{ partial "utils/content" . }} 10 |
11 | {{ end }} 12 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/utils/title.html: -------------------------------------------------------------------------------- 1 | {{ $title := "" }} 2 | 3 | {{ if .Title }} 4 | {{ $title = .Title }} 5 | {{ else if and .IsSection .File }} 6 | {{ $title = path.Base .File.Dir | humanize | title }} 7 | {{ else if and .IsPage .File }} 8 | {{ $title = .File.BaseFileName | humanize | title }} 9 | {{ end }} 10 | 11 | {{ return $title }} 12 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/before.yaml: -------------------------------------------------------------------------------- 1 | # https://bugs.mysql.com/bug.php?id=97001 2 | # https://blog.jcole.us/2019/09/30/reconsidering-access-paths-for-index-ordering-a-dangerous-optimization-and-a-fix/ 3 | # https://hackmysql.com/post/infamous-order-by-limit-query-optimizer-bug/ 4 | 5 | stage: 6 | trx: 7 | - file: get-1-row.sql 8 | workload: 9 | - iter: 1000 10 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ partial "page-header" . }} 3 | 4 | 5 |
8 |

{{ partial "utils/title" . }}

9 | {{ partial "utils/content" . }} 10 |
11 | {{ end }} 12 | -------------------------------------------------------------------------------- /aws/rds_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package aws 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestRDSCert(t *testing.T) { 10 | var err any 11 | f := func() { 12 | defer func() { 13 | err = recover() 14 | }() 15 | RegisterRDSCA() // panics on any error 16 | } 17 | f() 18 | if err != nil { 19 | t.Errorf("RegisterRDSCA paniced: %v", err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/docker/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | name: finch 3 | services: 4 | finch-mysql80: 5 | image: mysql:8.0.34 # LTS 6 | container_name: mysql-8.0.34 7 | command: --default-authentication-plugin=mysql_native_password 8 | restart: always 9 | environment: 10 | - "MYSQL_ROOT_PASSWORD=test" 11 | ports: 12 | - "33800:3306" 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/favicons.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/utils/content.html: -------------------------------------------------------------------------------- 1 | {{ $content := .Content }} 2 | 3 | {{ $content = $content | replaceRE `` `` | safeHTML }} 4 | {{ $content = $content | replaceRE `((?:.|\n)+?
)` `
${1}
` | safeHTML }} 5 | 6 | {{ return $content }} 7 | -------------------------------------------------------------------------------- /benchmarks/xfer/_all.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#xfer 2 | 3 | params: 4 | customers: 1,000,000 # + 3 balances per customer (4M rows total) 5 | active: 20 # % of customers between lower and upper 6 | lower: 500,000 7 | upper: 1,000,000 8 | xfers-size: 1GiB # about 2.3M rows 9 | clients: $sys.CPU_CORES # override on command line like "-p clients=2" 10 | 11 | stats: 12 | freq: 5s 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/foot.html: -------------------------------------------------------------------------------- 1 | {{ if default true .Site.Params.GeekdocSearch }} 2 | 3 | {{- $searchConfigFile := printf "search/%s.config.json" .Language.Lang -}} 4 | {{- $searchConfig := resources.Get "search/config.json" | resources.ExecuteAsTemplate $searchConfigFile . | resources.Minify -}} 5 | {{- $searchConfig.Publish -}} 6 | {{ end }} 7 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/theme.toml: -------------------------------------------------------------------------------- 1 | name = "Geekdoc" 2 | license = "MIT" 3 | licenselink = "https://github.com/thegeeklab/hugo-geekdoc/blob/main/LICENSE" 4 | description = "Hugo theme made for documentation" 5 | homepage = "https://geekdocs.de/" 6 | demosite = "https://geekdocs.de/" 7 | tags = ["docs", "documentation", "responsive", "simple"] 8 | min_version = "0.93.0" 9 | 10 | [author] 11 | name = "Robert Kaussow" 12 | homepage = "https://thegeeklab.de/" 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/tab.html: -------------------------------------------------------------------------------- 1 | {{- if .Parent }} 2 | {{- $name := .Get 0 }} 3 | {{- $group := printf "tabs-%s" (.Parent.Get 0) }} 4 | 5 | {{- if not (.Parent.Scratch.Get $group) }} 6 | {{- .Parent.Scratch.Set $group slice }} 7 | {{- end }} 8 | 9 | {{- .Parent.Scratch.Add $group (dict "Name" $name "Content" .Inner) }} 10 | {{- else }} 11 | {{ errorf "%q: 'tab' shortcode must be inside 'tabs' shortcode" .Page.Path }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/js/484-21d9ea41.chunk.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Wait for document loaded before starting the execution 3 | */ 4 | 5 | /*! @license DOMPurify 2.4.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.4.5/LICENSE */ 6 | 7 | /*! Check if previously processed */ 8 | 9 | /*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ 10 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/utils/featured.html: -------------------------------------------------------------------------------- 1 | {{ $img := "" }} 2 | 3 | {{ with $source := ($.Resources.ByType "image").GetMatch "{*feature*,*cover*,*thumbnail*}" }} 4 | {{ $featured := .Fill (printf "1200x630 %s" (default "Smart" .Params.anchor)) }} 5 | {{ $img = $featured.Permalink }} 6 | {{ else }} 7 | {{ with default $.Site.Params.images $.Params.images }} 8 | {{ $img = index . 0 | absURL }} 9 | {{ end }} 10 | {{ end }} 11 | 12 | {{ return $img }} 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/_markup/render-codeblock-mermaid.html: -------------------------------------------------------------------------------- 1 | 2 | {{ if not (.Page.Scratch.Get "mermaid") }} 3 | 4 | 5 | {{ .Page.Scratch.Set "mermaid" true }} 6 | {{ end }} 7 | 8 | 9 |
10 |   {{- .Inner -}}
11 | 
12 | -------------------------------------------------------------------------------- /benchmarks/xfer/trx/insert-cust-bal.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN 3 | 4 | -- prepare 5 | -- rows: $params.customers 6 | INSERT INTO customers VALUES (NULL, @c_token, 'USA', @c_c1, @c_c2, @c_c3, 1, NOW(), NOW()) 7 | 8 | -- prepare 9 | INSERT INTO balances VALUES 10 | (NULL, @b_token, @c_token, 1, 100, 'USD', @b_c1, @b_c2, 1, NOW(), NOW()), 11 | (NULL, @b_token, @c_token, 1, 200, 'USD', @b_c1, @b_c2, 1, NOW(), NOW()), 12 | (NULL, @b_token, @c_token, 1, 300, 'USD', @b_c1, @b_c2, 1, NOW(), NOW()) 13 | 14 | COMMIT 15 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/mermaid.html: -------------------------------------------------------------------------------- 1 | 2 | {{ if not (.Page.Scratch.Get "mermaid") }} 3 | 4 | 5 | {{ .Page.Scratch.Set "mermaid" true }} 6 | {{ end }} 7 | 8 | 9 |
10 |   {{- .Inner -}}
11 | 
12 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #efefef 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/utils/description.html: -------------------------------------------------------------------------------- 1 | {{ $isPage := or (and (ne .Type "posts") (in "section page" .Kind )) (and (eq .Type "posts") (eq .Kind "page")) }} 2 | {{ $description := "" }} 3 | 4 | {{ if .Description }} 5 | {{ $description = .Description }} 6 | {{ else }} 7 | {{ if $isPage }} 8 | {{ $description = .Summary }} 9 | {{ else if .Site.Params.description }} 10 | {{ $description = .Site.Params.description }} 11 | {{ end }} 12 | {{ end }} 13 | 14 | {{ return $description }} 15 | -------------------------------------------------------------------------------- /benchmarks/sysbench/trx/read-only.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN 3 | 4 | -- prepare 5 | -- copies: 10 6 | SELECT c FROM sbtest1 WHERE id=@id 7 | 8 | -- prepare 9 | SELECT c FROM sbtest1 WHERE id BETWEEN @id_100 AND @PREV 10 | 11 | -- prepare 12 | SELECT SUM(k) FROM sbtest1 WHERE id BETWEEN @id_100 AND @PREV 13 | 14 | -- prepare 15 | SELECT c FROM sbtest1 WHERE id BETWEEN @id_100 AND @PREV ORDER BY c 16 | 17 | -- prepare 18 | SELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN @id_100 AND @PREV ORDER BY c 19 | 20 | -- prepare 21 | COMMIT 22 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/_markup/render-link.html: -------------------------------------------------------------------------------- 1 | {{- $raw := or (hasPrefix .Text " 12 | {{- .Text | safeHTML -}} 13 | 14 | {{- /* Drop trailing newlines */ -}} 15 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/toc.html: -------------------------------------------------------------------------------- 1 | {{- $format := default "html" (.Get "format") }} 2 | {{- $tocLevels := default (default 6 .Site.Params.GeekdocToC) .Page.Params.GeekdocToC }} 3 | 4 | {{- if and $tocLevels .Page.TableOfContents -}} 5 | {{- if not (eq ($format | lower) "raw") -}} 6 |
7 | {{ .Page.TableOfContents }} 8 |
9 |
10 | {{- else -}} 11 | {{ .Page.TableOfContents }} 12 | {{- end -}} 13 | {{- end -}} 14 | -------------------------------------------------------------------------------- /benchmarks/sysbench/read-only.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#sysbench 2 | 3 | stage: 4 | name: read-only 5 | runtime: 60s 6 | workload: 7 | - clients: 1 8 | trx: 9 | - file: trx/read-only.sql 10 | data: 11 | id: 12 | generator: "int" 13 | params: 14 | max: $params.rows 15 | dist: normal 16 | id_100: 17 | generator: "int-range" 18 | params: 19 | range: 100 20 | max: $params.rows 21 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/posts/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |

{{ partial "utils/title" . }}

5 | 8 |
9 |
10 | {{ partial "utils/content" . }} 11 |
12 |
13 | {{ end }} 14 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/columns.html: -------------------------------------------------------------------------------- 1 | {{- $size := default "regular" (.Get "size" | lower) }} 2 | 3 | {{- if not (in (slice "regular" "large" "small") $size) }} 4 | {{- $size = "regular" }} 5 | {{- end }} 6 | 7 | 8 |
9 | {{- range split .Inner "<--->" }} 10 |
11 | {{ . | $.Page.RenderString -}} 12 |
13 | {{- end }} 14 |
15 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/print-735ccc12.min.css: -------------------------------------------------------------------------------- 1 | @media print{.gdoc-nav,.gdoc-footer .container span:not(:first-child),.gdoc-paging,.editpage{display:none}.gdoc-footer{border-top:1px solid #dee2e6}.gdoc-markdown pre{white-space:pre-wrap;overflow-wrap:break-word}.chroma code{border:1px solid #dee2e6;padding:.5rem !important;font-weight:normal !important}.gdoc-markdown code{font-weight:bold}a,a:visited{color:inherit !important;text-decoration:none !important}.gdoc-toc{flex:none}.gdoc-toc nav{position:relative;width:auto}.wrapper{display:block}.wrapper main{display:block}} 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/square/finch 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/alexflint/go-arg v1.4.3 7 | github.com/dustin/go-humanize v1.0.0 8 | github.com/go-ini/ini v1.67.0 9 | github.com/go-mysql/errors v0.0.0-20180603193453-03314bea68e0 10 | github.com/go-sql-driver/mysql v1.7.1 11 | github.com/go-test/deep v1.0.8 12 | github.com/rs/xid v1.4.0 13 | golang.org/x/time v0.3.0 14 | gopkg.in/yaml.v2 v2.4.0 15 | ) 16 | 17 | require ( 18 | github.com/alexflint/go-scalar v1.1.0 // indirect 19 | github.com/stretchr/testify v1.8.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /docs/static/custom.css: -------------------------------------------------------------------------------- 1 | html {scroll-behavior: auto;} /* No animated scrolling on link click */ 2 | code {overflow-x: scroll;} /* Horizontal scrolling for long code blocks */ 3 | 4 | :root, 5 | :root[color-theme="light"], 6 | :root[color-theme="dark"] 7 | { 8 | --mark-color: lightyellow; 9 | } 10 | 11 | .tagline { 12 | font-style: italic; 13 | } 14 | 15 | table.compact.params { 16 | width: auto !important; 17 | } 18 | 19 | .gdoc-markdown h3 > code { 20 | border: none !important; 21 | background-color: unset !important; 22 | font-size: 0.9em !important; 23 | } 24 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/assets/search/config.json: -------------------------------------------------------------------------------- 1 | {{- $searchDataFile := printf "search/%s.data.json" .Language.Lang -}} 2 | {{- $searchData := resources.Get "search/data.json" | resources.ExecuteAsTemplate $searchDataFile . | resources.Minify -}} 3 | { 4 | "dataFile": {{ $searchData.RelPermalink | jsonify }}, 5 | "indexConfig": {{ .Site.Params.GeekdocSearchConfig | jsonify }}, 6 | "showParent": {{ if .Site.Params.GeekdocSearchShowParent }}true{{ else }}false{{ end }}, 7 | "showDescription": {{ if .Site.Params.GeekdocSearchshowDescription }}true{{ else }}false{{ end }} 8 | } 9 | -------------------------------------------------------------------------------- /test/mock/stats_reporter.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "github.com/square/finch/stats" 5 | ) 6 | 7 | type StatsReporter struct { 8 | ReportFunc func([]stats.Instance) 9 | StopFunc func() 10 | } 11 | 12 | func (r StatsReporter) Make(name string, opts map[string]string) (stats.Reporter, error) { 13 | return r, nil 14 | } 15 | 16 | func (r StatsReporter) Report(from []stats.Instance) { 17 | if r.ReportFunc != nil { 18 | r.ReportFunc(from) 19 | } 20 | } 21 | 22 | func (r StatsReporter) Stop() { 23 | if r.StopFunc != nil { 24 | r.StopFunc() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/expand.html: -------------------------------------------------------------------------------- 1 | {{ $id := substr (sha1 .Inner) 0 8 }} 2 |
3 | 7 | 8 |
9 | {{ .Inner | $.Page.RenderString | htmlUnescape | safeHTML }} 10 |
11 |
12 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/assets/search/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | {{ range $index, $page := (where .Site.Pages "Params.GeekdocProtected" "ne" true) }} 3 | {{ if ne $index 0 }},{{ end }} 4 | { 5 | "id": {{ $index }}, 6 | "href": "{{ $page.RelPermalink }}", 7 | "title": {{ (partial "utils/title" $page) | jsonify }}, 8 | "parent": {{ with $page.Parent }}{{ (partial "utils/title" .) | jsonify }}{{ else }}""{{ end }}, 9 | "content": {{ $page.Plain | jsonify }}, 10 | "description": {{ $page.Summary | plainify | jsonify }} 11 | } 12 | {{ end }} 13 | ] 14 | -------------------------------------------------------------------------------- /benchmarks/aurora/setup.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#aurora 2 | 3 | stage: 4 | name: setup 5 | trx: 6 | - file: trx/create-tables.sql 7 | - file: trx/insert-rows.sql 8 | data: 9 | k: 10 | generator: "int" 11 | params: 12 | max: 65536 13 | c: 14 | generator: "str-fill-az" 15 | params: 16 | len: 119 # not a typo 17 | pad: 18 | generator: "str-fill-az" 19 | params: 20 | len: 59 # not a typo 21 | - file: trx/analyze-tables.sql 22 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/js/926-40cc1be5.chunk.min.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable 3 | Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com) 4 | Licensed under The MIT License (http://opensource.org/licenses/MIT) 5 | */ 6 | 7 | /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */ 8 | 9 | /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */ 10 | -------------------------------------------------------------------------------- /test/run/columns/setup.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS finch; 2 | 3 | USE finch; 4 | 5 | DROP TABLE IF EXISTS coltest1, coltest2, coltest3; 6 | 7 | CREATE TABLE coltest1 ( 8 | a int unsigned not null auto_increment primary key, 9 | b int not null default 0 10 | ); 11 | 12 | CREATE TABLE coltest2 ( 13 | x int unsigned not null default 0, 14 | y varchar(10) not null default "", 15 | z int not null default 0, 16 | primary key (x) 17 | ); 18 | 19 | CREATE TABLE coltest3 ( 20 | x int unsigned not null default 0, 21 | y varchar(10) not null default "", 22 | primary key (x) 23 | ); 24 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ hugo.Generator }} 6 | 7 | {{ $keywords := default .Site.Params.Keywords .Keywords }} 8 | 9 | {{- with partial "utils/description" . }} 10 | 11 | {{- end }} 12 | {{- with $keywords }} 13 | 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/after.yaml: -------------------------------------------------------------------------------- 1 | # https://bugs.mysql.com/bug.php?id=97001 2 | # https://blog.jcole.us/2019/09/30/reconsidering-access-paths-for-index-ordering-a-dangerous-optimization-and-a-fix/ 3 | # https://hackmysql.com/post/infamous-order-by-limit-query-optimizer-bug/ 4 | 5 | stage: 6 | trx: 7 | - file: shift-rows.sql # UPDATE t SET id=id+1000000 WHERE other_id = 555 8 | - file: get-1-row.sql 9 | workload: 10 | - group: shift 11 | trx: [shift-rows.sql] 12 | iter: 1 13 | disable-stats: true 14 | 15 | - group: after 16 | trx: [get-1-row.sql] 17 | iter: 2 18 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/microformats/twitter_cards.html: -------------------------------------------------------------------------------- 1 | {{- with partial "utils/featured" . }} 2 | 3 | {{- else }} 4 | 5 | {{- end }} 6 | 7 | {{- with partial "utils/featured" . }} 8 | 9 | {{- end }} 10 | {{- with partial "utils/description" . }} 11 | 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /benchmarks/jcole-bug-97001/setup.yaml: -------------------------------------------------------------------------------- 1 | # https://bugs.mysql.com/bug.php?id=97001 2 | # https://blog.jcole.us/2019/09/30/reconsidering-access-paths-for-index-ordering-a-dangerous-optimization-and-a-fix/ 3 | # https://hackmysql.com/post/infamous-order-by-limit-query-optimizer-bug/ 4 | 5 | stage: 6 | trx: 7 | - file: schema.sql 8 | - file: insert-rows.sql 9 | data: 10 | n: 11 | generator: auto-inc 12 | scope: client-group 13 | workload: 14 | - trx: [schema.sql] 15 | - trx: [insert-rows.sql] # 5k rows/insert 16 | iter-clients: 200 # x 200 inserts = 1,000,000 rows 17 | clients: 4 18 | -------------------------------------------------------------------------------- /benchmarks/sysbench/setup.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#sysbench 2 | 3 | stage: 4 | name: "setup" 5 | stats: 6 | disable: true 7 | trx: 8 | # Executed in order: 9 | - file: trx/schema.sql 10 | - file: trx/insert-rows.sql 11 | data: 12 | k: 13 | generator: "int" 14 | params: 15 | max: 65536 16 | c: 17 | generator: "str-fill-az" 18 | params: 19 | len: 119 # not a typo 20 | pad: 21 | generator: "str-fill-az" 22 | params: 23 | len: 59 # not a typo 24 | - file: trx/secondary-index.sql 25 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/hint.html: -------------------------------------------------------------------------------- 1 | {{ $type := default "note" (.Get "type") }} 2 | {{ $icon := .Get "icon" }} 3 | {{ $title := default ($type | title) (.Get "title") }} 4 | 5 | 6 |
7 |
8 | {{- with $icon -}} 9 | 10 | {{ $title }} 11 | {{- else -}} 12 | 13 | {{- end -}} 14 |
15 |
{{ .Inner | $.Page.RenderString }}
16 |
17 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/include.html: -------------------------------------------------------------------------------- 1 | {{ $file := .Get "file" }} 2 | {{ $page := .Site.GetPage $file }} 3 | {{ $type := .Get "type" }} 4 | {{ $language := .Get "language" }} 5 | {{ $options :=.Get "options" }} 6 | 7 | 8 |
9 | {{- if (.Get "language") -}} 10 | {{- highlight ($file | readFile) $language (default "linenos=table" $options) -}} 11 | {{- else if eq $type "html" -}} 12 | {{- $file | readFile | safeHTML -}} 13 | {{- else if eq $type "page" -}} 14 | {{- with $page }}{{ .Content }}{{ end -}} 15 | {{- else -}} 16 | {{- $file | readFile | $.Page.RenderString -}} 17 | {{- end -}} 18 |
19 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/search.html: -------------------------------------------------------------------------------- 1 | {{ if default true .Site.Params.GeekdocSearch }} 2 | 16 | {{ end }} 17 | -------------------------------------------------------------------------------- /docs/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = "/finch" 2 | title = "Finch Docs" 3 | theme = "hugo-geekdoc" 4 | languageCode = 'en-us' 5 | 6 | pluralizeListTitles = false 7 | 8 | # Geekdoc required configuration 9 | pygmentsUseClasses = true 10 | pygmentsCodeFences = true 11 | disablePathToLower = true 12 | 13 | # Required if you want to render robots.txt template 14 | enableRobotsTXT = true 15 | 16 | # Needed for mermaid shortcodes 17 | [markup] 18 | [markup.goldmark.renderer] 19 | # Needed for mermaid shortcode 20 | unsafe = true 21 | [markup.tableOfContents] 22 | startLevel = 1 23 | endLevel = 9 24 | [markup.goldmark.parser.attribute] 25 | block = true 26 | 27 | [taxonomies] 28 | tag = "tags" 29 | -------------------------------------------------------------------------------- /docs/content/api/data.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | Implement `data.Generator`: 5 | 6 | ```go 7 | type Generator interface { 8 | Format() (uint, string) 9 | Copy() Generator 10 | Values(RunCount) []interface{} 11 | Scan(any interface{}) error 12 | Name() string 13 | } 14 | ``` 15 | 16 | Your generator does _not_ have to handle data scope. 17 | When it's called, Finch expects new values. 18 | 19 | Implement `data.Factory` to create your data generator: 20 | 21 | ```go 22 | type Factory interface { 23 | Make(name, dataKey string, params map[string]string) (Generator, error) 24 | } 25 | ``` 26 | 27 | Register your data generator and its factory by calling `data.Register(name string, f Factory) error`. 28 | -------------------------------------------------------------------------------- /test/run/scope/workload_cg_alloc.yaml: -------------------------------------------------------------------------------- 1 | stage: 2 | name: test 3 | trx: 4 | - file: 001.sql 5 | data: 6 | a: 7 | scope: statement 8 | generator: auto-inc 9 | r: 10 | scope: statement 11 | generator: int 12 | params: 13 | dist: uniform 14 | max: 1,000,000 15 | clientid: 16 | scope: statement 17 | generator: client-id 18 | params: 19 | ids: exec-group,client-group,client,iter,trx,statement 20 | workload: 21 | # ###### Exec group __A__ ##### 22 | - clients: 2 # Client group 1 23 | group: __A__ # 24 | 25 | - clients: 2 # Client group 2 26 | group: __A__ # 27 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/tabs.html: -------------------------------------------------------------------------------- 1 | {{- if .Inner }}{{ end }} 2 | {{- $id := .Get 0 }} 3 | {{- $group := printf "tabs-%s" $id }} 4 | 5 | 6 |
7 | {{- range $index, $tab := .Scratch.Get $group }} 8 | 15 | 18 |
19 | {{ .Content | $.Page.RenderString }} 20 |
21 | {{- end }} 22 |
23 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/katex.html: -------------------------------------------------------------------------------- 1 | 2 | {{ if not (.Page.Scratch.Get "katex") }} 3 | 4 | 8 | 9 | {{ .Page.Scratch.Set "katex" true }} 10 | {{ end }} 11 | 12 | 13 | 14 | {{ cond (in .Params "display") "\\[" "\\(" -}} 15 | {{- trim .Inner "\n" -}} 16 | {{- cond (in .Params "display") "\\]" "\\)" -}} 17 | 18 | {{- /* Drop trailing newlines */ -}} 19 | -------------------------------------------------------------------------------- /limit/data_test.go: -------------------------------------------------------------------------------- 1 | package limit_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/square/finch/limit" 7 | ) 8 | 9 | func TestOr(t *testing.T) { 10 | r1 := limit.NewRows(100, 0) 11 | r2 := limit.NewRows(50, 0) 12 | 13 | dl := limit.Or(r1, r2) 14 | 15 | // We can pass nil for the *sql.DB because Rows doesn't use it for More 16 | if dl.More(nil) == false { 17 | t.Error("More false, expected true before anything called") 18 | } 19 | 20 | r1.Affected(1) // 1/100 21 | r2.Affected(1) // 1/50 22 | if dl.More(nil) != true { 23 | t.Error("More false, expected true before either limit reached") 24 | } 25 | 26 | r2.Affected(49) // 50/50 27 | if dl.More(nil) != false { 28 | t.Error("More true, expected false when one limit reached") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/button.html: -------------------------------------------------------------------------------- 1 | {{- $ref := "" }} 2 | {{- $class := "" }} 3 | {{- $size := default "regular" (.Get "size" | lower) }} 4 | 5 | {{- if not (in (slice "regular" "large") $size) }} 6 | {{- $size = "regular" }} 7 | {{- end }} 8 | 9 | {{- with .Get "href" }} 10 | {{- $ref = . }} 11 | {{- end }} 12 | 13 | {{- with .Get "relref" }} 14 | {{- $ref = relref $ . }} 15 | {{- end }} 16 | 17 | {{- with .Get "class" }} 18 | {{- $class = . }} 19 | {{- end }} 20 | 21 | 22 | 23 | 27 | {{ $.Inner }} 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/progress.html: -------------------------------------------------------------------------------- 1 | {{- $value := default 0 (.Get "value") -}} 2 | {{- $title := .Get "title" -}} 3 | {{- $icon := .Get "icon" -}} 4 | 5 | 6 |
7 |
8 |
9 | {{ with $icon -}} 10 | 11 | {{- end }} 12 | {{ with $title }}{{ . }}{{ end }} 13 |
14 |
{{ $value }}%
15 |
16 |
17 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /data/string_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package data_test 4 | 5 | import ( 6 | "strconv" 7 | "testing" 8 | 9 | "github.com/square/finch/data" 10 | ) 11 | 12 | func TestString_StrFillAz(t *testing.T) { 13 | lens := []string{"1", "10", "100", "1000", "10000", "100000", "1000000"} 14 | for _, strlen := range lens { 15 | g, _ := data.NewStrFillAz( 16 | map[string]string{ 17 | "len": strlen, 18 | }, 19 | ) 20 | 21 | r := data.RunCount{} 22 | 23 | v1 := g.Values(r) 24 | if len(v1) != 1 { 25 | t.Fatalf("len=%s: got %d values, expected 1: %v", strlen, len(v1), v1) 26 | } 27 | s1 := v1[0].(string) 28 | n, _ := strconv.Atoi(strlen) 29 | if len(s1) != n { 30 | t.Errorf("len=%s: got len %d, expected %s: %s", strlen, len(s1), strlen, s1) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/run/scope/statement.yaml: -------------------------------------------------------------------------------- 1 | stage: 2 | name: test 3 | trx: 4 | - file: 001.sql 5 | data: 6 | a: 7 | scope: statement 8 | generator: auto-inc 9 | r: 10 | scope: statement 11 | generator: int 12 | params: 13 | dist: uniform 14 | max: 1,000,000 15 | clientid: 16 | scope: statement 17 | generator: client-id 18 | params: 19 | ids: exec-group,client-group,client,iter,trx,statement 20 | - file: 002.sql 21 | # Same data keys as 001.sql 22 | workload: 23 | # ###### Exec group __A__ ##### 24 | - group: __A__ 25 | clients: 1 26 | iter: 2 27 | - group: __A__ 28 | clients: 1 29 | iter: 1 30 | - group: __B__ 31 | clients: 1 32 | iter: 1 33 | -------------------------------------------------------------------------------- /benchmarks/sysbench/write-only.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#sysbench 2 | 3 | stage: 4 | name: write-only 5 | runtime: 60s 6 | workload: 7 | - clients: 1 8 | trx: 9 | - file: trx/write-only.sql 10 | data: 11 | id: 12 | generator: "int" 13 | params: 14 | max: $params.rows 15 | dist: normal 16 | del_id: 17 | generator: "int" 18 | scope: trx 19 | params: 20 | max: $params.rows 21 | dist: normal 22 | k: 23 | generator: "int" 24 | params: 25 | max: 65536 26 | c: 27 | generator: "str-fill-az" 28 | params: 29 | len: 119 30 | pad: 31 | generator: "str-fill-az" 32 | params: 33 | len: 59 34 | -------------------------------------------------------------------------------- /compute/server_test.go: -------------------------------------------------------------------------------- 1 | package compute_test 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/square/finch/compute" 9 | "github.com/square/finch/config" 10 | "github.com/square/finch/test" 11 | ) 12 | 13 | func TestServer_Run1Stage(t *testing.T) { 14 | if test.Build { 15 | t.Skip("GitHub Actions build") 16 | } 17 | 18 | dsn, db, err := test.Connection() 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | defer db.Close() 23 | 24 | os.Setenv("PORT", test.MySQLPort) 25 | stages, err := config.Load( 26 | []string{"../test/run/select-1/test.yaml"}, 27 | []string{}, 28 | dsn, 29 | "", // default db 30 | ) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | s := compute.NewServer("local", "", false) 36 | 37 | err = s.Run(context.Background(), stages) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/run/scope/client.yaml: -------------------------------------------------------------------------------- 1 | stage: 2 | name: test 3 | trx: 4 | - file: 001.sql 5 | data: 6 | a: 7 | scope: client ######### 8 | generator: auto-inc # 9 | r: # 10 | scope: client ######### 11 | generator: int 12 | params: 13 | dist: uniform 14 | max: 1,000,000 15 | clientid: 16 | scope: statement 17 | generator: client-id 18 | params: 19 | ids: exec-group,client-group,client,iter,trx,statement 20 | - file: 002.sql 21 | # Same data keys as 001.sql 22 | workload: 23 | # ###### Exec group __A__ ##### 24 | - group: __A__ 25 | clients: 1 26 | iter: 2 27 | - group: __A__ 28 | clients: 1 29 | iter: 1 30 | - group: __B__ 31 | clients: 1 32 | iter: 1 33 | -------------------------------------------------------------------------------- /test/run/scope/iter.yaml: -------------------------------------------------------------------------------- 1 | stage: 2 | name: test 3 | trx: 4 | - file: 001.sql 5 | data: 6 | a: 7 | scope: iter ########### 8 | generator: auto-inc # 9 | r: # 10 | scope: iter ########### 11 | generator: int 12 | params: 13 | dist: uniform 14 | max: 1,000,000 15 | clientid: 16 | scope: statement 17 | generator: client-id 18 | params: 19 | ids: exec-group,client-group,client,iter,trx,statement 20 | - file: 002.sql 21 | # Same data keys as 001.sql 22 | workload: 23 | # ###### Exec group __A__ ##### 24 | - group: __A__ 25 | clients: 1 26 | iter: 2 27 | - group: __A__ 28 | clients: 1 29 | iter: 1 30 | - group: __B__ 31 | clients: 1 32 | iter: 1 33 | -------------------------------------------------------------------------------- /test/run/scope/trx.yaml: -------------------------------------------------------------------------------- 1 | stage: 2 | name: test 3 | trx: 4 | - file: 001.sql 5 | data: 6 | a: 7 | scope: trx ############ 8 | generator: auto-inc # 9 | r: # 10 | scope: trx ############ 11 | generator: int 12 | params: 13 | dist: uniform 14 | max: 1,000,000 15 | clientid: 16 | scope: statement 17 | generator: client-id 18 | params: 19 | ids: exec-group,client-group,client,iter,trx,statement 20 | - file: 002.sql 21 | # Same data keys as 001.sql 22 | workload: 23 | # ###### Exec group __A__ ##### 24 | - group: __A__ 25 | clients: 1 26 | iter: 2 27 | - group: __A__ 28 | clients: 1 29 | iter: 1 30 | - group: __B__ 31 | clients: 1 32 | iter: 1 33 | -------------------------------------------------------------------------------- /docs/content/syntax/values.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 4 3 | title: Values 4 | --- 5 | 6 | ## String-int 7 | 8 | In Finch [stage]({{< relref "syntax/stage-file" >}}) and [\_all.yaml]({{< relref "syntax/all-file" >}}) files, most numerical values are YAML strings. 9 | 10 | |String Value|Numerical Value| 11 | |------------|---------------| 12 | |""|0 or default value| 13 | |"1"|1| 14 | |"1,000"|1000| 15 | |"1k"|1000| 16 | |"1KB"|1000| 17 | |"1KiB"|1024| 18 | |"$params.foo"|5 when `params.foo = "5"`| 19 | 20 | ## Time Duration 21 | 22 | Time durations (usually a configuration or parameter called `freq`) are [Go time duration](https://pkg.go.dev/time#ParseDuration) strings: 23 | 24 | >Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". 25 | 26 | ## String-bool 27 | 28 | These strings evaluate to boolean true: 29 | 30 | * "true" 31 | * "yes" 32 | * "on" 33 | * "aye" 34 | 35 | All other strings, including empty strings, evaluate to boolean false. 36 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/pagination.html: -------------------------------------------------------------------------------- 1 | {{ $pag := $.Paginator }} 2 | 3 | 4 | 23 | -------------------------------------------------------------------------------- /docs/content/benchmark/error-handling.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 6 3 | --- 4 | 5 | Finch handles these MySQL errors _without_ reconnecting: 6 | 7 | |Error|MySQL Error Code|Handling| 8 | |-----|----------------|--------| 9 | |Deadlock|1213|MySQL automatically rolls back| 10 | |Lock wait timeout|1205|Execute `ROLLBACK` because `innodb_rollback_on_timeout=OFF` by default| 11 | |Query killed|1317|| 12 | |Read-only|1290, 1836|| 13 | |Duplicate key|1062|| 14 | 15 | After handling the errors above, Finch starts a new iteration from the first [assigned trx]({{< relref "benchmark/workload#trx" >}}). 16 | 17 | Other errors cause Finch to disconnect and reconnect to MySQL, then start a new iteration. 18 | Reconnect time is not directly measured or recorded, but if it's severe it will reduce reported throughput because Finch will spend time reconnecting rather than executing queries. 19 | 20 | Query [statistics]({{< relref "benchmark/statistics" >}}) are recorded when the query returns an error. 21 | This is usually correct because, for example, a lock wait timeout is part of query response time. 22 | However, for errors that cause a fast error-retry-error loop, it will skew statistics towards zero or artificially high values. 23 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Robert Kaussow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice (including the next 13 | paragraph) shall be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 21 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/_markup/render-heading.html: -------------------------------------------------------------------------------- 1 | {{- $showAnchor := (and (default true .Page.Params.GeekdocAnchor) (default true .Page.Site.Params.GeekdocAnchor)) -}} 2 | 3 | 4 | 5 | {{- if $showAnchor -}} 6 |
7 | 11 | {{ .Text | safeHTML }} 12 | 13 | 14 | 15 | 16 |
17 | {{- else -}} 18 |
19 | 23 | {{ .Text | safeHTML }} 24 | 25 |
26 | {{- end -}} 27 | 28 | -------------------------------------------------------------------------------- /test/mysql.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | // Build is true when running in GitHub Actions. When true, database tests are 11 | // skipped because currently we don't run MySQL in GitHub Acitons, but other tests 12 | // and the Go build still run. 13 | var Build = os.Getenv("GITHUB_ACTION") != "" 14 | 15 | var MySQLPort = "33800" // test/docker/docker-compose.yaml 16 | 17 | func Connection() (string, *sql.DB, error) { 18 | dsn := fmt.Sprintf( 19 | "%s:%s@tcp(%s:%s)/?parseTime=true", 20 | "root", 21 | "test", 22 | "127.0.0.1", 23 | MySQLPort, 24 | ) 25 | db, err := sql.Open("mysql", dsn) 26 | if err != nil { 27 | return "", nil, err 28 | } 29 | if err := db.Ping(); err != nil { 30 | db.Close() 31 | return "", nil, err 32 | } 33 | return dsn, db, nil 34 | } 35 | 36 | func OneRow(db *sql.DB, query string) (string, error) { 37 | var s string 38 | err := db.QueryRowContext(context.Background(), query).Scan(&s) 39 | return s, err 40 | } 41 | 42 | func Exec(db *sql.DB, queries []string) error { 43 | for _, q := range queries { 44 | if _, err := db.Exec(q); err != nil { 45 | return fmt.Errorf("%s: %s", q, err) 46 | } 47 | } 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /docs/content/intro/tldr.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 3 3 | title: "TL;DR" 4 | --- 5 | 6 | * Finch benchmarks MySQL; other databases are not currently supported. 7 | * Benchmarks are written in SQL; write your queries in a `.sql` file. 8 | * Each SQL file is a [Finch trx]({{< relref "syntax/trx-file" >}})—"trx" for short. 9 | * Trx should [model]({{< relref "benchmark/trx" >}}) real MySQL transactions, but they don't have to. 10 | * In SQL statements, [data keys]({{< relref "data/keys" >}}) like @d inject fake/random/test data. 11 | * Data keys are configured in [stage files]({{< relref "syntax/stage-file" >}}) to use a [data generator]({{< relref "data/generators" >}}). 12 | * Data generators are plugins that generate data (e.g. random numbers). 13 | * At least one stage file is required to configure and run Finch. 14 | * A benchmark comprises one or more stages (defined by stage files). 15 | * After writing SQL files with data keys, write a stage file to configure and execute. 16 | * [Statistics]({{< relref "benchmark/statistics" >}}) are reported once at the end by default; periodic stats can be enabled. 17 | * Stats reporters are plugins, so you can report stats any way, any where. 18 | * Look in `benchmarks/sysbench/` for a complete and familiar example. 19 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/terms.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ range .Paginator.Pages.ByTitle }} 3 |
4 |
5 |

6 | {{ partial "utils/title" . }} 7 |

8 |
9 | 10 |
11 | 12 | {{ $pageCount := len .Pages }} 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | {{ $latet := index .Pages.ByDate 0 }} 23 | {{ with $latet }} 24 | {{ partial "utils/title" . }} 25 | {{ end }} 26 | 27 | 28 |
29 |
30 | {{ end }} 31 | {{ partial "pagination.html" . }} 32 | {{ end }} 33 | -------------------------------------------------------------------------------- /stage/stage_test.go: -------------------------------------------------------------------------------- 1 | package stage 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/square/finch/config" 8 | "github.com/square/finch/data" 9 | "github.com/square/finch/test" 10 | ) 11 | 12 | func TestPreapre_NoWorkload(t *testing.T) { 13 | if test.Build { 14 | t.Skip("GitHub Actions build") 15 | } 16 | 17 | dsn, db, err := test.Connection() 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | defer db.Close() 22 | 23 | cfg := config.Stage{ 24 | Name: "test", 25 | Trx: []config.Trx{ 26 | { 27 | Name: "001", 28 | File: "../test/trx/001.sql", 29 | Data: map[string]config.Data{ 30 | "id": { 31 | Generator: "int", 32 | }, 33 | }, 34 | }, 35 | }, 36 | MySQL: config.MySQL{ 37 | DSN: dsn, 38 | }, 39 | Workload: []config.ClientGroup{}, // no workload 40 | } 41 | gds := data.NewScope() // global data scope 42 | 43 | s := New(cfg, gds, nil) 44 | err = s.Prepare(context.Background()) 45 | if err != nil { 46 | t.Error(err) 47 | } 48 | 49 | // There should be 1 exec group, 1 client group, and 1 client 50 | if len(s.execGroups) != 1 { 51 | t.Fatalf("got %d exec group, expected 1", len(s.execGroups)) 52 | } 53 | if len(s.execGroups[0]) != 1 { 54 | t.Fatalf("got %d clients, expected 1", len(s.execGroups[0])) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /benchmarks/xfer/trx/xfer.sql: -------------------------------------------------------------------------------- 1 | 2 | BEGIN 3 | 4 | -- save-columns: @sender_token, @country 5 | SELECT c_token, country FROM customers WHERE id=@sender_id -- 2 6 | 7 | -- save-columns: @sender_balance_token 8 | SELECT b_token FROM balances WHERE c_token=@sender_token LIMIT 1 -- 3 9 | 10 | -- save-columns: @receiver_token 11 | SELECT c_token FROM customers WHERE id BETWEEN @receiver_id AND @receiver_id+100 AND country=@country LIMIT 1 -- 4 12 | 13 | -- save-columns: @receiver_balance_token 14 | SELECT b_token FROM balances WHERE c_token=@receiver_token LIMIT 1 -- 5 15 | 16 | -- save-insert-id: @xfer_id 17 | INSERT INTO xfers VALUES (NULL, @x_token, 100, 'USD', @sender_balance_token, @receiver_balance_token, 1, @c1, @c2, @c3, NOW(), NULL, NULL, 1, 0, NOW(), NOW()) -- 6 18 | 19 | -- save-columns: @sender_balance_id, _ 20 | SELECT id, version FROM balances WHERE b_token=@sender_balance_token FOR UPDATE -- 7 21 | 22 | UPDATE balances SET cents=cents-100, version=version+1 WHERE id=@sender_balance_id -- 8 23 | 24 | -- save-columns: @receiver_balance_id, _ 25 | SELECT id, version FROM balances WHERE b_token=@receiver_balance_token FOR UPDATE -- 9 26 | 27 | UPDATE balances SET cents=cents+100, version=version+1 WHERE id=@receiver_balance_id -- 10 28 | 29 | UPDATE xfers SET t2=NOW(), c3='DONE' WHERE id=@xfer_id -- 11 30 | 31 | COMMIT 32 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/mobile-79ddc617.min.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 41rem){.gdoc-nav{margin-left:-18rem;font-size:16px}.gdoc-nav__control{display:inline-block}.gdoc-header svg.gdoc-icon{width:1.5rem;height:1.5rem}.gdoc-brand{font-size:1.5rem;line-height:1.5rem}.gdoc-brand__img{display:none}.gdoc-menu-header__items{display:none}.gdoc-menu-header__control,.gdoc-menu-header__home{display:flex}.gdoc-error{padding:6rem 1rem}.gdoc-error svg.gdoc-icon{width:6rem;height:6rem}.gdoc-error__message{padding-left:2rem}.gdoc-error__line{padding:.25rem 0}.gdoc-error__title{font-size:2rem}.gdoc-page__header .breadcrumb,.hidden-mobile{display:none}.flex-mobile-column{flex-direction:column}.flex-mobile-column.gdoc-columns{margin:2rem 0}.flex-mobile-column .gdoc-columns__content{min-width:auto;margin:0}#menu-control:checked~main .gdoc-nav nav,#menu-control:checked~main .gdoc-page{transform:translateX(18rem)}#menu-control:checked~main .gdoc-page{opacity:.25}#menu-control:checked~.gdoc-header .gdoc-nav__control svg.gdoc-icon.gdoc_menu{display:none}#menu-control:checked~.gdoc-header .gdoc-nav__control svg.gdoc-icon.gdoc_arrow_back{display:inline-block}#menu-header-control:checked~.gdoc-header .gdoc-brand{display:none}#menu-header-control:checked~.gdoc-header .gdoc-menu-header__items{display:flex}#menu-header-control:checked~.gdoc-header .gdoc-menu-header__control svg.gdoc-icon.gdoc_keyboard_arrow_left{display:none}} 2 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ partial "head/meta" . }} 5 | {{ i18n "error_page_title" }} 6 | 7 | {{ partial "head/favicons" . }} 8 | {{ partial "head/others" . }} 9 | 10 | 11 | 12 | {{ partial "svg-icon-symbols" . }} 13 | 14 | 15 |
16 | 17 | 18 | {{ partial "site-header" (dict "Root" . "MenuEnabled" false) }} 19 | 20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 |
{{ i18n "error_message_title" }}
28 |
{{ i18n "error_message_code" }}
29 |
30 | {{ i18n "error_message_text" .Site.BaseURL | safeHTML }} 31 |
32 |
33 |
34 |
35 | 36 | {{ partial "site-footer" . }} 37 | 38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/zh-cn.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: 编辑页面 3 | 4 | nav_navigation: 导航 5 | nav_tags: 标签 6 | nav_more: 更多 7 | nav_top: 回到顶部 8 | 9 | form_placeholder_search: 搜索 10 | 11 | error_page_title: 迷路了? 不用担心 12 | error_message_title: 迷路了? 13 | error_message_code: 错误 404 14 | error_message_text: > 15 | 好像找不到你要找的东西。 别担心,我们可以 16 | 带您回到主页。 17 | 18 | button_toggle_dark: 切换暗/亮/自动模式 19 | button_nav_open: 打开导航 20 | button_nav_close: 关闭导航 21 | button_menu_open: 打开菜单栏 22 | button_menu_close: 关闭菜单栏 23 | button_homepage: 返回首页 24 | 25 | title_anchor_prefix: "锚定到:" 26 | 27 | posts_read_more: 阅读全文 28 | posts_read_time: 29 | one: "一分钟阅读时间" 30 | other: "{{ . }} 分钟阅读时间" 31 | posts_update_prefix: 更新时间 32 | posts_count: 33 | one: 一篇文章 34 | other: "{{ . }} 个帖子" 35 | posts_tagged_with: 所有带有“{{ . }}”标签的帖子。 36 | 37 | footer_build_with: > 38 | 基于 Hugo 39 | 制作 40 | footer_legal_notice: "法律声明" 41 | footer_privacy_policy: "隐私政策" 42 | footer_content_license_prefix: > 43 | 内容许可证 44 | 45 | language_switch_no_tranlation_prefix: "页面未翻译:" 46 | 47 | propertylist_required: 需要 48 | propertylist_optional: 可选 49 | propertylist_default: 默认值 50 | 51 | pagination_page_prev: 以前 52 | pagination_page_next: 下一个 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/menu-extra.html: -------------------------------------------------------------------------------- 1 | {{ $current := .current }} 2 | {{ template "menu-extra" dict "sect" .source "current" $current "site" $current.Site "target" .target }} 3 | 4 | 5 | 6 | {{ define "menu-extra" }} 7 | {{ $current := .current }} 8 | {{ $site := .site }} 9 | {{ $target := .target }} 10 | {{ $sect := .sect }} 11 | 12 | {{ range sort (default (seq 0) $sect) "weight" }} 13 | {{ if isset . "ref" }} 14 | {{ $this := $site.GetPage .ref }} 15 | {{ $isCurrent := eq $current $this }} 16 | {{ $icon := default false .icon }} 17 | 18 | {{ $name := .name }} 19 | {{ if reflect.IsMap .name }} 20 | {{ $name = (index .name $site.Language.Lang) }} 21 | {{ end }} 22 | 23 | {{ if not .icon }} 24 | {{ errorf "Missing 'icon' attribute in data file for '%s' menu item '%s'" $target $name }} 25 | {{ end }} 26 | 27 | {{ if eq $target "header" }} 28 | 29 | 37 | 38 | {{ $name }} 39 | 40 | 41 | 42 | 43 | {{ end }} 44 | {{ end }} 45 | {{ end }} 46 | {{ end }} 47 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/posts/list.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ range .Paginator.Pages }} 3 | 31 | {{ end }} 32 | {{ partial "pagination.html" . }} 33 | {{ end }} 34 | 35 | {{ define "post-tag" }} 36 | 47 | {{ end }} 48 | -------------------------------------------------------------------------------- /benchmarks/sysbench/_all.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#sysbench 2 | 3 | # This base config, _all.yaml, applies to all stages in the same directory. 4 | # It allows only the following 3 config sections: 5 | 6 | # __ 01 _____________________________________________________________________ 7 | # 8 | # MySQL usually applies to all stages because there's usually only 1 MySQL instance. 9 | # But it could differ if, for example, a setup stage inserts rows on a writable 10 | # source, and then benchmarks are run on a read-only replica. 11 | # 12 | # For consistency with other Finch example benchmarks, though, let's leave this 13 | # commented out and use the default Finch MySQL username and password. 14 | #mysql: 15 | # dsn: "root@tcp(127.0.0.1:3306)/sbtest" 16 | 17 | # __ 02 _____________________________________________________________________ 18 | # 19 | # Params usually apply to all stages to keep key settings in sync across stages 20 | # and trx (SQL) files, like number of tables and rows. Each param is referenced 21 | # like $params.rows or "${params.rows}". (YAML requires quotes around {}.) 22 | params: 23 | rows: "10,000" # You can use human-readable numbers like this or "10k" 24 | 25 | # __ 03 _____________________________________________________________________ 26 | # 27 | # Stats don't normally apply to all stages, but it's show here for demonstration. 28 | # A stage can override this to turn off stats by setting stage.stats.disable=true, 29 | # as shown in setup.yaml. 30 | stats: 31 | freq: 5s 32 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/taxonomy.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ range .Paginator.Pages }} 3 | 33 | {{ end }} 34 | {{ partial "pagination.html" . }} 35 | {{ end }} 36 | 37 | {{ define "post-tag" }} 38 | 49 | {{ end }} 50 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/ja.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: ページの編集 3 | 4 | nav_navigation: ナビゲーション 5 | nav_tags: タグ 6 | nav_more: さらに 7 | nav_top: トップへ戻る 8 | 9 | form_placeholder_search: 検索 10 | 11 | error_page_title: お困りですか?ご心配なく 12 | error_message_title: お困りですか? 13 | error_message_code: 404 エラー 14 | error_message_text: > 15 | お探しのものが見つからないようです。トップページ 16 | へ戻ることができるので、ご安心ください。 17 | 18 | button_toggle_dark: モードの切替 ダーク/ライト/自動 19 | button_nav_open: ナビゲーションを開く 20 | button_nav_close: ナビゲーションを閉じる 21 | button_menu_open: メニューバーを開く 22 | button_menu_close: メニューバーを閉じる 23 | button_homepage: トップページへ戻る 24 | 25 | title_anchor_prefix: "アンカー先:" 26 | 27 | posts_read_more: 全投稿を閲覧 28 | posts_read_time: 29 | one: "読むのに 1 分かかります" 30 | other: "読むのに要する時間 {{ . }} (分)" 31 | posts_update_prefix: 更新時刻 32 | posts_count: 33 | one: "一件の投稿" 34 | other: "{{ . }} 件の投稿" 35 | posts_tagged_with: "'{{ . }}'のタグが付いた記事全部" 36 | 37 | footer_build_with: > 38 | Hugo でビルドしています。 39 | 40 | footer_legal_notice: 法的な告知事項 41 | footer_privacy_policy: プライバシーポリシー 42 | footer_content_license_prefix: > 43 | 提供するコンテンツのライセンス 44 | 45 | language_switch_no_tranlation_prefix: "未翻訳のページ:" 46 | 47 | propertylist_required: 必須 48 | propertylist_optional: 任意 49 | propertylist_default: 既定値 50 | 51 | pagination_page_prev: 前 52 | pagination_page_next: 次 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/menu.html: -------------------------------------------------------------------------------- 1 | 45 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/toc-tree.html: -------------------------------------------------------------------------------- 1 | {{- $tocLevels := default (default 6 .Site.Params.GeekdocToC) .Page.Params.GeekdocToC }} 2 | 3 | {{- if $tocLevels }} 4 |
5 | {{ template "toc-tree" dict "sect" .Page.Pages }} 6 |
7 | {{- end }} 8 | 9 | 10 | 11 | {{- define "toc-tree" }} 12 | 39 | {{- end }} 40 | -------------------------------------------------------------------------------- /test/mock/data_generator.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/square/finch/data" 7 | ) 8 | 9 | type DataGeneratorFactory struct { 10 | MakeFunc func(name, dataKey string, params map[string]string) (data.Generator, error) 11 | } 12 | 13 | func (f DataGeneratorFactory) Make(name, dataKey string, params map[string]string) (data.Generator, error) { 14 | if f.MakeFunc != nil { 15 | return f.MakeFunc(name, dataKey, params) 16 | } 17 | return nil, fmt.Errorf("MakeFunc not set in mock.DataGeneratorFactory") 18 | } 19 | 20 | var _ data.Factory = DataGeneratorFactory{} 21 | 22 | type DataGenerator struct { 23 | FormatFunc func() (uint, string) 24 | CopyFunc func() data.Generator 25 | ValuesFunc func(data.RunCount) []interface{} 26 | ScanFunc func(any interface{}) error 27 | NameFunc func() string 28 | } 29 | 30 | var _ data.Generator = DataGenerator{} 31 | 32 | func (g DataGenerator) Format() (uint, string) { 33 | if g.FormatFunc != nil { 34 | return g.FormatFunc() 35 | } 36 | return 0, "" 37 | } 38 | 39 | func (g DataGenerator) Copy() data.Generator { 40 | if g.CopyFunc != nil { 41 | return g.CopyFunc() 42 | } 43 | return g 44 | } 45 | 46 | func (g DataGenerator) Values(rc data.RunCount) []interface{} { 47 | if g.ValuesFunc != nil { 48 | return g.ValuesFunc(rc) 49 | } 50 | return nil 51 | } 52 | 53 | func (g DataGenerator) Scan(any interface{}) error { 54 | if g.ScanFunc != nil { 55 | return g.ScanFunc(any) 56 | } 57 | return nil 58 | } 59 | 60 | func (g DataGenerator) Name() string { 61 | if g.NameFunc != nil { 62 | return g.NameFunc() 63 | } 64 | return "mock" 65 | } 66 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/en.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: Edit page 3 | 4 | nav_navigation: Navigation 5 | nav_tags: Tags 6 | nav_more: More 7 | nav_top: Back to top 8 | 9 | form_placeholder_search: Search 10 | 11 | error_page_title: Lost? Don't worry 12 | error_message_title: Lost? 13 | error_message_code: Error 404 14 | error_message_text: > 15 | Seems like what you are looking for can't be found. Don't worry, we can 16 | bring you back to the homepage. 17 | 18 | button_toggle_dark: Toggle Dark/Light/Auto mode 19 | button_nav_open: Open Navigation 20 | button_nav_close: Close Navigation 21 | button_menu_open: Open Menu Bar 22 | button_menu_close: Close Menu Bar 23 | button_homepage: Back to homepage 24 | 25 | title_anchor_prefix: "Anchor to:" 26 | 27 | posts_read_more: Read full post 28 | posts_read_time: 29 | one: "One minute to read" 30 | other: "{{ . }} minutes to read" 31 | posts_update_prefix: Updated on 32 | posts_count: 33 | one: "One post" 34 | other: "{{ . }} posts" 35 | posts_tagged_with: All posts tagged with '{{ . }}' 36 | 37 | footer_build_with: > 38 | Built with Hugo and 39 | 40 | footer_legal_notice: Legal Notice 41 | footer_privacy_policy: Privacy Policy 42 | footer_content_license_prefix: > 43 | Content licensed under 44 | 45 | language_switch_no_tranlation_prefix: "Page not translated:" 46 | 47 | propertylist_required: required 48 | propertylist_optional: optional 49 | propertylist_default: default 50 | 51 | pagination_page_prev: prev 52 | pagination_page_next: next 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/posts/metadata.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {{ $tc := 0 }} 19 | {{ with .Params.tags }} 20 | {{ range sort . }} 21 | {{ $name := . }} 22 | {{ with $.Site.GetPage (printf "/tags/%s" $name | urlize) }} 23 | {{ if eq $tc 0 }} 24 | 25 | 26 | {{ template "post-tag" dict "name" $name "page" . }} 27 | 28 | {{ else }} 29 | 30 | {{ template "post-tag" dict "name" $name "page" . }} 31 | 32 | {{ end }} 33 | {{ end }} 34 | {{ $tc = (add $tc 1) }} 35 | {{ end }} 36 | {{ end }} 37 | 38 | {{ define "post-tag" }} 39 | 48 | {{ end }} 49 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/nl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: Wijzig pagina 3 | 4 | nav_navigation: Navigatie 5 | nav_tags: Markering 6 | nav_more: Meer 7 | nav_top: Terug naar boven 8 | 9 | form_placeholder_search: Zoek 10 | 11 | error_page_title: Verdwaald? Geen probleem 12 | error_message_title: Verdwaald? 13 | error_message_code: Error 404 14 | error_message_text: > 15 | Het lijkt er op dat wat je zoekt niet gevonden kan worden. Geen probleem, 16 | we kunnen je terug naar de startpagina brengen. 17 | 18 | button_toggle_dark: Wijzig Donker/Licht/Auto weergave 19 | button_nav_open: Open navigatie 20 | button_nav_close: Sluit navigatie 21 | button_menu_open: Open menubalk 22 | button_menu_close: Sluit menubalk 23 | button_homepage: Terug naar startpagina 24 | 25 | title_anchor_prefix: "Link naar:" 26 | 27 | posts_read_more: Lees volledige bericht 28 | posts_read_time: 29 | one: "Een minuut leestijd" 30 | other: "{{ . }} minuten leestijd" 31 | posts_update_prefix: Bijgewerkt op 32 | posts_count: 33 | one: "Een bericht" 34 | other: "{{ . }} berichten" 35 | posts_tagged_with: Alle berichten gemarkeerd met '{{ . }}' 36 | 37 | footer_build_with: > 38 | Gebouwd met Hugo en 39 | 40 | footer_legal_notice: Juridische mededeling 41 | footer_privacy_policy: Privacybeleid 42 | footer_content_license_prefix: > 43 | Inhoud gelicenseerd onder 44 | 45 | language_switch_no_tranlation_prefix: "Pagina niet vertaald:" 46 | 47 | propertylist_required: verplicht 48 | propertylist_optional: optioneel 49 | propertylist_default: standaard 50 | 51 | pagination_page_prev: vorige 52 | pagination_page_next: volgende 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/de.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: Seite bearbeiten 3 | 4 | nav_navigation: Navigation 5 | nav_tags: Tags 6 | nav_more: Weitere 7 | nav_top: Nach oben 8 | 9 | form_placeholder_search: Suchen 10 | 11 | error_page_title: Verlaufen? Keine Sorge 12 | error_message_title: Verlaufen? 13 | error_message_code: Fehler 404 14 | error_message_text: > 15 | Wir können die Seite nach der Du gesucht hast leider nicht finden. Keine Sorge, 16 | wir bringen Dich zurück zur Startseite. 17 | 18 | button_toggle_dark: Wechsel zwischen Dunkel/Hell/Auto Modus 19 | button_nav_open: Navigation öffnen 20 | button_nav_close: Navigation schließen 21 | button_menu_open: Menüband öffnen 22 | button_menu_close: Menüband schließen 23 | button_homepage: Zurück zur Startseite 24 | 25 | title_anchor_prefix: "Link zu:" 26 | 27 | posts_read_more: Ganzen Artikel lesen 28 | posts_read_time: 29 | one: "Eine Minute Lesedauer" 30 | other: "{{ . }} Minuten Lesedauer" 31 | posts_update_prefix: Aktualisiert am 32 | posts_count: 33 | one: "Ein Artikel" 34 | other: "{{ . }} Artikel" 35 | posts_tagged_with: Alle Artikel mit dem Tag '{{ . }}' 36 | 37 | footer_build_with: > 38 | Entwickelt mit Hugo und 39 | 40 | footer_legal_notice: Impressum 41 | footer_privacy_policy: Datenschutzerklärung 42 | footer_content_license_prefix: > 43 | Inhalt lizensiert unter 44 | 45 | language_switch_no_tranlation_prefix: "Seite nicht übersetzt:" 46 | 47 | propertylist_required: erforderlich 48 | propertylist_optional: optional 49 | propertylist_default: Standardwert 50 | 51 | pagination_page_prev: vorher 52 | pagination_page_next: weiter 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/cs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: Upravit stránku 3 | 4 | nav_navigation: Navigace 5 | nav_tags: Tagy 6 | nav_more: Více 7 | nav_top: Zpět nahoru 8 | 9 | form_placeholder_search: Vyhledat 10 | 11 | error_page_title: Ztracen? Nic se neděje 12 | error_message_title: Ztracen? 13 | error_message_code: Error 404 14 | error_message_text: > 15 | Vypadá to že stránka, kterou hledáte, neexistuje. Nemějte obavy, můžete 16 | se vrátit zpět na domovskou stránku. 17 | 18 | button_toggle_dark: Přepnout tmavý/světlý/automatický režim 19 | button_nav_open: Otevřít navigaci 20 | button_nav_close: Zavřít navigaci 21 | button_menu_open: Otevřít lištu nabídky 22 | button_menu_close: Zavřít lištu nabídky 23 | button_homepage: Zpět na domovskou stránku 24 | 25 | title_anchor_prefix: "Odkaz na:" 26 | 27 | posts_read_more: Přečíst celý příspěvek 28 | posts_read_time: 29 | one: "Doba čtení: 1 minuta" 30 | other: "Doba čtení: {{ . }} minut(y)" 31 | posts_update_prefix: Naposledy upraveno 32 | posts_count: 33 | one: "Jeden příspěvek" 34 | other: "Příspěvků: {{ . }}" 35 | posts_tagged_with: Všechny příspěvky označeny '{{ . }}' 36 | 37 | footer_build_with: > 38 | Vytvořeno za pomocí Hugo a 39 | 40 | footer_legal_notice: Právní upozornění 41 | footer_privacy_policy: Zásady ochrany soukromí 42 | footer_content_license_prefix: > 43 | Obsah licencovaný pod 44 | 45 | language_switch_no_tranlation_prefix: "Stránka není přeložena:" 46 | 47 | propertylist_required: povinné 48 | propertylist_optional: volitené 49 | propertylist_default: výchozí 50 | 51 | pagination_page_prev: předchozí 52 | pagination_page_next: další 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": null, 3 | "short_name": null, 4 | "description": null, 5 | "dir": "auto", 6 | "lang": "en-US", 7 | "display": "standalone", 8 | "orientation": "any", 9 | "scope": "", 10 | "start_url": "/?homescreen=1", 11 | "background_color": "#efefef", 12 | "theme_color": "#efefef", 13 | "icons": [ 14 | { 15 | "src": "/favicon/android-chrome-36x36.png", 16 | "sizes": "36x36", 17 | "type": "image/png", 18 | "purpose": "any" 19 | }, 20 | { 21 | "src": "/favicon/android-chrome-48x48.png", 22 | "sizes": "48x48", 23 | "type": "image/png", 24 | "purpose": "any" 25 | }, 26 | { 27 | "src": "/favicon/android-chrome-72x72.png", 28 | "sizes": "72x72", 29 | "type": "image/png", 30 | "purpose": "any" 31 | }, 32 | { 33 | "src": "/favicon/android-chrome-96x96.png", 34 | "sizes": "96x96", 35 | "type": "image/png", 36 | "purpose": "any" 37 | }, 38 | { 39 | "src": "/favicon/android-chrome-144x144.png", 40 | "sizes": "144x144", 41 | "type": "image/png", 42 | "purpose": "any" 43 | }, 44 | { 45 | "src": "/favicon/android-chrome-192x192.png", 46 | "sizes": "192x192", 47 | "type": "image/png", 48 | "purpose": "any" 49 | }, 50 | { 51 | "src": "/favicon/android-chrome-256x256.png", 52 | "sizes": "256x256", 53 | "type": "image/png", 54 | "purpose": "any" 55 | }, 56 | { 57 | "src": "/favicon/android-chrome-384x384.png", 58 | "sizes": "384x384", 59 | "type": "image/png", 60 | "purpose": "any" 61 | }, 62 | { 63 | "src": "/favicon/android-chrome-512x512.png", 64 | "sizes": "512x512", 65 | "type": "image/png", 66 | "purpose": "any" 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/_default/baseof.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | {{ partial "head/meta" . }} 9 | 10 | {{- if eq .Kind "home" -}} 11 | {{ .Site.Title }} 12 | {{- else -}} 13 | {{ printf "%s | %s" (partial "utils/title" .) .Site.Title }} 14 | {{- end -}} 15 | 16 | 17 | {{ partial "head/favicons" . }} 18 | {{ partial "head/rel-me" . }} 19 | {{ partial "head/microformats" . }} 20 | {{ partial "head/others" . }} 21 | {{ partial "head/custom" . }} 22 | 23 | 24 | 25 | {{ partial "svg-icon-symbols" . }} 26 | 27 | 28 |
31 | 32 | 33 | {{ $navEnabled := default true .Page.Params.GeekdocNav }} 34 | {{ partial "site-header" (dict "Root" . "MenuEnabled" $navEnabled) }} 35 | 36 | 37 |
38 | {{ if $navEnabled }} 39 | 42 | {{ end }} 43 | 44 | 45 |
46 | {{ template "main" . }} 47 | 48 | 49 | 52 |
53 |
54 | 55 | {{ partial "site-footer" . }} 56 |
57 | 58 | {{ partial "foot" . }} 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/i18n/it.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | edit_page: Modifica la pagina 3 | 4 | nav_navigation: Navigazione 5 | nav_tags: Etichette 6 | nav_more: Altro 7 | nav_top: Torna su 8 | 9 | form_placeholder_search: Cerca 10 | 11 | error_page_title: Perso? Non ti preoccupare 12 | error_message_title: Perso? 13 | error_message_code: Errore 404 14 | error_message_text: > 15 | Sembra che non sia possibile trovare quello che stavi cercando. Non ti preoccupare, 16 | possiamo riportarti alla pagina iniziale. 17 | 18 | button_toggle_dark: Seleziona il tema Chiaro/Scuro/Automatico 19 | button_nav_open: Apri la Navigazione 20 | button_nav_close: Chiudi la Navigazione 21 | button_menu_open: Apri la Barra del Menu 22 | button_menu_close: Chiudi la Barra del Menu 23 | button_homepage: Torna alla pagina iniziale 24 | 25 | title_anchor_prefix: "Ancora a:" 26 | 27 | posts_read_more: Leggi tutto il post 28 | posts_read_time: 29 | one: "Tempo di lettura: un minuto" 30 | other: "Tempo di lettura: {{ . }} minuti" 31 | posts_update_prefix: Aggiornato il 32 | posts_count: 33 | one: "Un post" 34 | other: "{{ . }} post" 35 | posts_tagged_with: Tutti i post etichettati con '{{ . }}' 36 | 37 | footer_build_with: > 38 | Realizzato con Hugo e 39 | 40 | footer_legal_notice: Avviso Legale 41 | footer_privacy_policy: Politica sulla Privacy 42 | footer_content_license_prefix: > 43 | Contenuto sotto licenza 44 | 45 | language_switch_no_tranlation_prefix: "Pagina non tradotta:" 46 | 47 | propertylist_required: richiesto 48 | propertylist_optional: opzionale 49 | propertylist_default: valore predefinito 50 | 51 | pagination_page_prev: precedente 52 | pagination_page_next: prossimo 53 | pagination_page_state: "{{ .PageNumber }}/{{ .TotalPages }}" 54 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/language.html: -------------------------------------------------------------------------------- 1 | {{ if hugo.IsMultilingual }} 2 | 3 |
    4 |
  • 5 | {{ range .Site.Languages }} 6 | {{ if eq . $.Site.Language }} 7 | 8 | 9 | {{ .Lang | upper }} 10 | 11 | {{ end }} 12 | {{ end }} 13 | 14 | 15 | 48 |
  • 49 |
50 |
51 | {{ end }} 52 | -------------------------------------------------------------------------------- /benchmarks/aurora/write-only.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#aurora 2 | 3 | stage: 4 | name: "write-only" 5 | runtime: 600s 6 | # ######################################################################### 7 | # The distributed compute is a key asspect of this benchmark: the original 8 | # 2015 Aurora benchmark was run on four r3.8xlarge EC2 instances, with each 9 | # running 1,000 clients. 10 | compute: 11 | instances: $params.instances 12 | workload: 13 | - clients: $params.clients 14 | # ######################################################################### 15 | stats: 16 | report: 17 | stdout: 18 | percentiles: "95" 19 | each-instance: true 20 | combined: true 21 | trx: 22 | - file: trx/write-only.sql 23 | data: 24 | tableNo: # table number to query 25 | generator: "int" 26 | scope: trx # must be trx-scoped 27 | params: 28 | max: $params.tables 29 | dist: uniform 30 | id: # primary key 31 | generator: "int" 32 | params: 33 | max: $params.rows 34 | dist: uniform 35 | del_id: # primary key that's deleted and re-inserted 36 | generator: "int" 37 | scope: trx # must be trx-scoped; spans last 2 queries 38 | params: 39 | max: $params.rows 40 | dist: uniform 41 | k: # random int, non-unique secondary index 42 | generator: "int" 43 | params: 44 | max: 65536 # higher max = more keys 45 | c: # random string data (not indexed) 46 | generator: "str-fill-az" 47 | params: 48 | len: 119 # 119 1-byte chars + \0 byte = char(120) 49 | pad: # random string data (not indexed) 50 | generator: "str-fill-az" 51 | params: 52 | len: 59 # 59 1-byte chars + \0 byte = char(60) 53 | 54 | -------------------------------------------------------------------------------- /benchmarks/xfer/setup.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#xfer 2 | 3 | stage: 4 | name: setup 5 | stats: 6 | disable: true 7 | workload: 8 | - clients: 1 # 9 | group: schema # exec group "schema" 10 | trx: [schema.sql] # 11 | - clients: 2 # 12 | group: rows # 13 | trx: [insert-cust-bal.sql] # exec group "rows" 14 | - clients: 2 # 15 | group: rows # 16 | trx: [insert-xfers.sql] # 17 | 18 | trx: 19 | - file: ./trx/schema.sql 20 | - file: ./trx/insert-cust-bal.sql 21 | data: 22 | c_token: 23 | generator: xid 24 | scope: trx 25 | b_token: 26 | generator: xid 27 | scope: value # required to generate 3 different balance tokens in same INSERT statement 28 | c_c1: 29 | generator: "str-fill-az" 30 | params: 31 | len: 20 32 | c_c2: 33 | generator: "str-fill-az" 34 | params: 35 | len: 50 36 | c_c3: 37 | generator: "str-fill-az" 38 | params: 39 | len: 75 40 | b_c1: 41 | generator: "str-fill-az" 42 | params: 43 | len: 30 44 | b_c2: 45 | generator: "str-fill-az" 46 | params: 47 | len: 50 48 | - file: ./trx/insert-xfers.sql 49 | data: 50 | x_token: 51 | generator: "xid" 52 | s_token: 53 | generator: "xid" 54 | r_token: 55 | generator: "xid" 56 | c1: 57 | generator: "str-fill-az" 58 | params: 59 | len: 10 60 | c2: 61 | generator: "str-fill-az" 62 | params: 63 | len: 50 64 | c3: 65 | generator: "str-fill-az" 66 | params: 67 | len: 5 68 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/site-footer.html: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /docs/content/syntax/params.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 5 3 | --- 4 | 5 | There are three types of parameters: user-defined, built-in, and environment variable. 6 | 7 | {{< toc >}} 8 | 9 | ## User-defined 10 | 11 | User-defined parameters are defined in [\_all.yaml]({{< relref "syntax/all-file#params" >}}), and [stage files]({{< relref "syntax/stage-file" >}}), and by [`--param`]({{< relref "operate/command-line#--param" >}}) on the command line. 12 | 13 | ```yaml 14 | params: 15 | foo: "bar" 16 | rows: "100k" 17 | ``` 18 | 19 | * "$params.foo" → "bar" 20 | * "$params.rows" → "100000" 21 | 22 | The "$params." prefix is required. 23 | It can be wrapped in curly braces: "${params.foo}". 24 | 25 | ## Built-in 26 | 27 | |Param|Value| 28 | |----|------| 29 | |$sys.CPU_CORES|Number of CPU cores detected by Go| 30 | 31 | Built-in parameters are used as shown in the table above (no "$params." prefix). 32 | 33 | ## Environment Variable 34 | 35 | If a parameter isn't user-defined or built-in, Finch tries to fetch it as an environment variable. 36 | For example, if `HOME=/home/finch`, then "$HOME" → "/home/finch". 37 | 38 | ## Inheritance 39 | 40 | {{< columns >}} 41 | _\_all.yaml_ 42 | ```yaml 43 | params: 44 | foo: "bar" 45 | rows: "100k" 46 | ``` 47 | <---> 48 | _+ Stage File_ 49 | ```yaml 50 | stage: 51 | params: 52 | rows: "500" 53 | clients: 16 54 | ``` 55 | <---> 56 | _= Final Params_ 57 | ``` 58 | foo = bar 59 | rows = 500 60 | clients = 16 61 | ``` 62 | {{< /columns >}} 63 | 64 | [`--param`]({{< relref "operate/command-line#--param" >}}) on the command line overrides all and sets the final value. 65 | 66 | Reusing the example above, `--params rows=999` overrides both \_all.yaml and the stage file, setting final value rows = 999. 67 | 68 | {{< hint type=tip >}} 69 | Look for "param:" in the [`--debug`]({{< relref "operate/command-line#--debug" >}}) output to debug parameters processing. 70 | {{< /hint >}} 71 | -------------------------------------------------------------------------------- /benchmarks/xfer/xfer.yaml: -------------------------------------------------------------------------------- 1 | # https://square.github.io/finch/benchmark/examples/#xfer 2 | 3 | stage: 4 | name: xfer 5 | runtime: 60s 6 | workload: 7 | - clients: $params.clients 8 | trx: 9 | - file: ./trx/xfer.sql 10 | data: 11 | # Select a random sender 12 | sender_id: 13 | generator: "int-gaps" 14 | params: 15 | p: $params.active 16 | min: $params.lower 17 | max: $params.upper 18 | sender_token: 19 | generator: column 20 | params: 21 | quote-value: yes 22 | country: 23 | generator: column 24 | params: 25 | quote-value: yes 26 | sender_balance_token: 27 | generator: column 28 | params: 29 | quote-value: yes 30 | sender_balance_id: # sender_balance_token -> customers.id 31 | generator: column 32 | 33 | # Select a random receiver 34 | receiver_id: 35 | generator: "int-gaps" 36 | params: 37 | p: $params.active 38 | min: $params.lower 39 | max: $params.upper 40 | receiver_token: 41 | generator: column 42 | params: 43 | quote-value: yes 44 | receiver_balance_token: 45 | generator: column 46 | params: 47 | quote-value: yes 48 | receiver_balance_id: # receiver_balance_token -> customers.id 49 | generator: column 50 | 51 | # Insert and update a transfer record between sender and receiver 52 | x_token: 53 | generator: xid 54 | xfer_id: 55 | generator: column 56 | c1: 57 | generator: "str-fill-az" 58 | params: 59 | len: 25 60 | c2: 61 | generator: "str-fill-az" 62 | params: 63 | len: 75 64 | c3: 65 | generator: "str-fill-az" 66 | params: 67 | len: 10 68 | 69 | -------------------------------------------------------------------------------- /dbconn/factory_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package dbconn_test 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | 9 | "github.com/go-sql-driver/mysql" 10 | 11 | "github.com/square/finch/config" 12 | "github.com/square/finch/dbconn" 13 | "github.com/square/finch/test" 14 | ) 15 | 16 | func TestMake_DSN(t *testing.T) { 17 | if test.Build { 18 | t.Skip("GitHub Actions build") 19 | } 20 | 21 | // Test connection 22 | dsn, db, err := test.Connection() 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | defer db.Close() 27 | 28 | // Make factory-made connect with test DSN 29 | cfg := config.MySQL{ 30 | DSN: dsn, 31 | } 32 | dbconn.SetConfig(cfg) 33 | 34 | fdb, fdsn, err := dbconn.Make() 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | 39 | if fdb == nil { 40 | t.Fatal("got nil *sql.DB") 41 | } 42 | defer fdb.Close() 43 | 44 | if fdsn != strings.Replace(dsn, "test", "...", 1) { // factory DSN redacts password to "..." 45 | t.Errorf("got dsn '%s', expected '%s'", fdsn, dsn) 46 | } 47 | 48 | } 49 | 50 | func TestMake_Config(t *testing.T) { 51 | if test.Build { 52 | t.Skip("GitHub Actions build") 53 | } 54 | 55 | // Test connection 56 | dsn, db, err := test.Connection() 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | defer db.Close() 61 | 62 | // Parse test DSN so we can map its parts to a Finch config.MySQL 63 | my, err := mysql.ParseDSN(dsn) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | // Make factory-made connect with test DSN 69 | cfg := config.MySQL{ 70 | Hostname: my.Addr, // has ":test.MySQLPort" suffix 71 | Password: my.Passwd, 72 | Username: my.User, 73 | } 74 | dbconn.SetConfig(cfg) 75 | 76 | fdb, _, err := dbconn.Make() 77 | if err != nil { 78 | t.Error(err) 79 | } 80 | if fdb == nil { 81 | t.Fatal("got nil *sql.DB") 82 | } 83 | defer fdb.Close() 84 | 85 | got, err := test.OneRow(fdb, "SELECT @@version") 86 | if err != nil { 87 | t.Error(err) 88 | } 89 | if got != "8.0.34" { 90 | t.Errorf("SELECT @@version: got %s, expected 8.0.34", got) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /data/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package data 4 | 5 | import ( 6 | "fmt" 7 | "math/rand" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // StrFillAz implemnts the str-fill-az data generator. 13 | type StrFillAz struct { 14 | len int64 15 | src rand.Source 16 | } 17 | 18 | var _ Generator = &StrFillAz{} 19 | 20 | // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go 21 | const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 22 | const ( 23 | letterIdxBits = 6 // 6 bits to represent a letter index 24 | letterIdxMask = 1<= 1") 38 | } 39 | return g, nil 40 | } 41 | 42 | func (g *StrFillAz) Name() string { return "str-fill-az" } 43 | func (g *StrFillAz) Format() (uint, string) { return 1, "'%s'" } 44 | func (g *StrFillAz) Scan(any interface{}) error { return nil } 45 | 46 | func (g *StrFillAz) Copy() Generator { 47 | return &StrFillAz{ 48 | len: g.len, 49 | src: rand.NewSource(time.Now().UnixNano()), 50 | } 51 | } 52 | 53 | func (g *StrFillAz) Values(_ RunCount) []interface{} { 54 | sb := strings.Builder{} 55 | sb.Grow(int(g.len)) 56 | // A src.Int63() generates 63 random bits, enough for letterIdxMax characters! 57 | for i, cache, remain := g.len-1, g.src.Int63(), letterIdxMax; i >= 0; { 58 | if remain == 0 { 59 | cache, remain = g.src.Int63(), letterIdxMax 60 | } 61 | if idx := int(cache & letterIdxMask); idx < len(letterBytes) { 62 | sb.WriteByte(letterBytes[idx]) 63 | i-- 64 | } 65 | cache >>= letterIdxBits 66 | remain-- 67 | } 68 | return []interface{}{sb.String()} 69 | } 70 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/propertylist.html: -------------------------------------------------------------------------------- 1 | {{- $name := .Get "name" -}} 2 | {{- $sort := .Get "sort" -}} 3 | {{- $order := default "asc" (.Get "order") -}} 4 | 5 | {{- if .Site.Data.properties }} 6 |
7 | {{- with (index .Site.Data.properties (split $name ".")) }} 8 | {{- $properties := .properties }} 9 | {{- with $sort }} 10 | {{- $properties = (sort $properties . $order) }} 11 | {{- end }} 12 | {{- range $properties }} 13 |
14 | {{ .name }} 15 | {{- if .required }} 16 | {{ i18n "propertylist_required" | lower }} 17 | {{ else }} 18 | {{ i18n "propertylist_optional" | lower }} 19 | {{- end }} 20 | {{- with .type }} 21 | {{ . }} 22 | {{- end }} 23 | 24 | {{- with .tags }} 25 | {{- $tags := . }} 26 | {{- if reflect.IsMap $tags }} 27 | {{- $tags = (index $tags $.Site.Language.Lang) }} 28 | {{- end }} 29 | {{- range $tags }} 30 | {{ . }} 31 | {{- end }} 32 | {{- end }} 33 |
34 |
35 |
36 | {{- with .description }} 37 | {{- $desc := . }} 38 | {{- if reflect.IsMap $desc }} 39 | {{- $desc = (index $desc $.Site.Language.Lang) }} 40 | {{- end }} 41 | 42 | {{ $desc | $.Page.RenderString }} 43 | {{ end }} 44 |
45 |
46 | {{- with default "none" (.defaultValue | string) }} 47 | {{ i18n "propertylist_default" | title }}: 48 | {{ . }} 49 | {{- end }} 50 |
51 |
52 | {{- end }} 53 | {{- end }} 54 |
55 | {{- end }} 56 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/head/others.html: -------------------------------------------------------------------------------- 1 | {{- if default true .Site.Params.GeekdocDarkModeToggle }} 2 | 3 | {{- end }} 4 | 5 | 6 | 13 | 20 | 21 | 26 | 31 | 32 | 37 | 42 | 43 | 48 | 53 | 54 | 59 | 64 | 65 | {{- with .OutputFormats.Get "html" }} 66 | {{ printf `` .Permalink .Rel .MediaType.Type | safeHTML }} 67 | {{- end }} 68 | 69 | {{- if (default false $.Site.Params.GeekdocOverwriteHTMLBase) }} 70 | 71 | {{- end }} 72 | 73 | {{ printf "" "Made with Geekdoc theme https://github.com/thegeeklab/hugo-geekdoc" | safeHTML }} 74 | -------------------------------------------------------------------------------- /stats/reporter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package stats_test 4 | 5 | import ( 6 | "os" 7 | "testing" 8 | 9 | "github.com/go-test/deep" 10 | 11 | "github.com/square/finch/stats" 12 | ) 13 | 14 | func TestParsePercentiles(t *testing.T) { 15 | tests := []struct { 16 | in string 17 | s []string 18 | p []float64 19 | }{ 20 | {"", stats.DefaultPercentileNames, stats.DefaultPercentiles}, 21 | {"95", []string{"P95"}, []float64{95.0}}, 22 | {"95,99", []string{"P95", "P99"}, []float64{95.0, 99.0}}, 23 | {"P99.9", []string{"P99.9"}, []float64{99.9}}, 24 | } 25 | for _, test := range tests { 26 | t.Run(test.in, func(t *testing.T) { 27 | s, p, err := stats.ParsePercentiles(test.in) 28 | if err != nil { 29 | t.Error(err) 30 | } 31 | if diff := deep.Equal(s, test.s); diff != nil { 32 | t.Error(diff) 33 | } 34 | if diff := deep.Equal(p, test.p); diff != nil { 35 | t.Error(diff) 36 | } 37 | 38 | }) 39 | } 40 | } 41 | 42 | func TestCSV(t *testing.T) { 43 | r, err := stats.NewCSV(map[string]string{}) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | 48 | file := r.File() 49 | t.Logf("stats file: %s", file) 50 | 51 | s := stats.NewStats() 52 | s.Record(stats.READ, 110) 53 | s.Record(stats.READ, 190) 54 | s.Record(stats.WRITE, 210) 55 | s.Record(stats.WRITE, 290) 56 | s.Record(stats.COMMIT, 310) 57 | s.Record(stats.COMMIT, 390) 58 | 59 | from := []stats.Instance{ 60 | { 61 | Hostname: "local", 62 | Clients: 1, 63 | Interval: 1, 64 | Seconds: 2.0, 65 | Runtime: 2.0, 66 | Total: s, 67 | //Trx: map[string]*stats.Stats{}, 68 | }, 69 | } 70 | r.Report(from) 71 | r.Stop() 72 | 73 | got, err := os.ReadFile(file) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | expect := `interval,duration,runtime,clients,QPS,min,P999,max,r_QPS,r_min,r_P999,r_max,w_QPS,w_min,w_P999,w_max,TPS,c_min,c_P999,c_max,errors,compute 78 | 1,2.0,2.0,1,3,110,389,390,1,110,185,190,1,210,294,290,1,310,389,390,0,local 79 | ` 80 | if string(got) != expect { 81 | t.Errorf("got:\n%s\nexpected:\n%s\n", string(got), expect) 82 | } 83 | 84 | err = os.Remove(file) 85 | if err != nil { 86 | t.Error(err) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /boot/cmdline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package boot 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/alexflint/go-arg" 9 | 10 | "github.com/square/finch" 11 | ) 12 | 13 | // Options represents the command line options 14 | type Options struct { 15 | Client string `arg:"env:FINCH_CLIENT"` 16 | CPUProfile string `arg:"--cpu-profile,env:FINCH_CPU_PROFILE"` 17 | Database string `arg:"-D,--database,env:FINCH_DB"` 18 | Debug bool `arg:"env:FINCH_DEBUG"` 19 | DSN string `arg:"env:FINCH_DSN"` 20 | Help bool 21 | Params []string `arg:"-p,--param,separate"` 22 | Server string `arg:"env:FINCH_SERVER"` 23 | Test bool `arg:"env:FINCH_TEST"` 24 | Version bool 25 | } 26 | 27 | type CommandLine struct { 28 | Options 29 | Args []string `arg:"positional"` 30 | } 31 | 32 | func ParseCommandLine(args []string) (CommandLine, error) { 33 | var c CommandLine 34 | p, err := arg.NewParser(arg.Config{Program: "finch"}, &c) 35 | if err != nil { 36 | return c, err 37 | } 38 | if err := p.Parse(args); err != nil { 39 | switch err { 40 | case arg.ErrHelp: 41 | c.Help = true 42 | case arg.ErrVersion: 43 | c.Version = true 44 | default: 45 | return c, fmt.Errorf("Error parsing command line: %s\n", err) 46 | } 47 | } 48 | return c, nil 49 | } 50 | 51 | func printHelp() { 52 | fmt.Printf("Usage:\n"+ 53 | " finch [options] STAGE_1_FILE [STAGE_N_FILE...]\n\n"+ 54 | "Options:\n"+ 55 | " --client ADDR[:PORT] Run as client of server at ADDR\n"+ 56 | " --cpu-profile FILE Save CPU profile of stage execution to FILE\n"+ 57 | " --database (-D) DB Default database on connect\n"+ 58 | " --debug Print debug output to stderr\n"+ 59 | " --dsn DSN MySQL DSN (overrides stage files)\n"+ 60 | " --help Print help and exit\n"+ 61 | " --param (-p) KEY=VAL Set param key=value (override stage files)\n"+ 62 | " --server ADDR[:PORT] Run as server on ADDR\n"+ 63 | " --test Validate stages, test connections, and exit\n"+ 64 | " --version Print version and exit\n"+ 65 | "\n"+ 66 | "Docs:\n"+ 67 | " https://square.github.io/finch/\n\n"+ 68 | "finch %s\n", 69 | finch.VERSION, 70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /data/column.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package data 4 | 5 | import ( 6 | "bytes" 7 | "database/sql" 8 | 9 | "github.com/square/finch" 10 | ) 11 | 12 | // Column is a special Generator that is used to save (Scan) values from rows 13 | // or insert ID, then return those values (Value) to other statements. 14 | type Column struct { 15 | quoteValue bool 16 | val interface{} 17 | bytes *bytes.Buffer 18 | useBytes bool 19 | } 20 | 21 | var _ Generator = &Column{} 22 | var _ sql.Scanner = &Column{} 23 | 24 | func NewColumn(params map[string]string) *Column { 25 | return &Column{ 26 | quoteValue: finch.Bool(params["quote-value"]), 27 | } 28 | } 29 | 30 | func (g *Column) Name() string { return "column" } 31 | 32 | func (g *Column) Format() (uint, string) { 33 | if g.quoteValue { 34 | return 1, "'%v'" 35 | } 36 | return 1, "%v" 37 | } 38 | 39 | func (g *Column) Copy() Generator { 40 | return &Column{ 41 | quoteValue: g.quoteValue, 42 | } 43 | } 44 | 45 | func (g *Column) Scan(any interface{}) error { 46 | // @todo column type won't change, so maybe sync.Once to set val or bytes 47 | // will make this more efficient? 48 | switch any.(type) { 49 | case []byte: 50 | g.useBytes = true // is reference; copy bytes 51 | if g.bytes == nil { 52 | g.bytes = bytes.NewBuffer(make([]byte, len(any.([]byte)))) 53 | } 54 | g.bytes.Reset() 55 | g.bytes.Write(any.([]byte)) 56 | default: 57 | g.useBytes = false // not reference; copy value 58 | g.val = any 59 | } 60 | return nil 61 | } 62 | 63 | func (g *Column) Values(_ RunCount) []interface{} { 64 | if g.useBytes { 65 | return []interface{}{g.bytes.String()} 66 | } 67 | return []interface{}{g.val} 68 | } 69 | 70 | // -------------------------------------------------------------------------- 71 | 72 | var Noop = noop{} 73 | 74 | type noop struct{} 75 | 76 | var _ Generator = Noop 77 | var _ sql.Scanner = Noop 78 | 79 | func (g noop) Name() string { return "no-op" } 80 | func (g noop) Format() (uint, string) { return 0, "" } 81 | func (g noop) Copy() Generator { return Noop } 82 | func (g noop) Values(_ RunCount) []interface{} { return nil } 83 | func (g noop) Scan(any interface{}) error { return nil } 84 | -------------------------------------------------------------------------------- /benchmarks/xfer/trx/schema.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE customers ( 3 | id bigint NOT NULL AUTO_INCREMENT, 4 | c_token varbinary(255) NOT NULL, 5 | country char(3) NOT NULL, 6 | c1 varchar(20) DEFAULT NULL, 7 | c2 varchar(50) DEFAULT NULL, 8 | c3 varchar(255) DEFAULT NULL, 9 | b1 tinyint NOT NULL, 10 | created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 12 | PRIMARY KEY (id), 13 | UNIQUE KEY (c_token), 14 | KEY (country) 15 | ) ENGINE=InnoDB; 16 | 17 | CREATE TABLE balances ( 18 | id bigint NOT NULL AUTO_INCREMENT, 19 | b_token varbinary(255) NOT NULL, 20 | c_token varbinary(255) NOT NULL, 21 | version int NOT NULL DEFAULT '0', 22 | cents bigint NOT NULL, 23 | currency varbinary(3) NOT NULL, 24 | c1 varchar(50) NOT NULL, 25 | c2 varchar(120) DEFAULT NULL, 26 | b1 tinyint NOT NULL, 27 | created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 28 | updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 29 | PRIMARY KEY (id), 30 | UNIQUE KEY (b_token), 31 | KEY (c_token) 32 | ) ENGINE=InnoDB; 33 | 34 | CREATE TABLE xfers ( 35 | id bigint NOT NULL AUTO_INCREMENT, 36 | x_token varchar(255) NOT NULL, 37 | cents bigint NOT NULL, 38 | currency varbinary(3) NOT NULL, 39 | s_token varchar(255) NOT NULL, 40 | r_token varchar(255) NOT NULL, 41 | version int unsigned NOT NULL DEFAULT '0', 42 | c1 varchar(50) DEFAULT NULL, 43 | c2 varchar(255) DEFAULT NULL, 44 | c3 varchar(30) DEFAULT NULL, 45 | t1 timestamp NULL DEFAULT NULL, 46 | t2 timestamp NULL DEFAULT NULL, 47 | t3 timestamp NULL DEFAULT NULL, 48 | b1 tinyint NOT NULL, 49 | b2 tinyint NOT NULL, 50 | created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 51 | updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 52 | PRIMARY KEY (id), 53 | UNIQUE KEY (x_token), 54 | KEY (s_token, t1), 55 | KEY (r_token, t1), 56 | KEY (created_at) 57 | ) ENGINE=InnoDB; 58 | -------------------------------------------------------------------------------- /finch_test.go: -------------------------------------------------------------------------------- 1 | package finch_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/square/finch" 7 | ) 8 | 9 | func TestUint(t *testing.T) { 10 | tests := []struct { 11 | input string 12 | output uint 13 | }{ 14 | {input: "", output: 0}, 15 | {input: "0", output: 0}, 16 | {input: "1", output: 1}, 17 | {input: "10", output: 10}, 18 | {input: "-10", output: 0}, // error ignore, 0 returned 19 | {input: "x", output: 0}, // error ignore, 0 returned 20 | } 21 | for _, test := range tests { 22 | t.Run("Uint("+test.input+")", func(t *testing.T) { 23 | got := finch.Uint(test.input) 24 | if got != test.output { 25 | t.Errorf("%s -> %d, expected %d", test.input, got, test.output) 26 | } 27 | }) 28 | } 29 | } 30 | 31 | func TestWithPort(t *testing.T) { 32 | port := "1234" 33 | tests := []struct { 34 | input string 35 | output string 36 | }{ 37 | {input: "", output: ":1234"}, 38 | {input: "0", output: "0:1234"}, 39 | {input: "local", output: "local:" + port}, 40 | {input: "local:1234", output: "local:" + port}, // same port, no change 41 | {input: "local:5678", output: "local:5678"}, // differnet port, no change 42 | } 43 | for _, test := range tests { 44 | t.Run("WithPort("+test.input+")", func(t *testing.T) { 45 | got := finch.WithPort(test.input, port) 46 | if got != test.output { 47 | t.Errorf("%s -> %s, expected %s", test.input, got, test.output) 48 | } 49 | }) 50 | } 51 | } 52 | 53 | func TestRunLevelGreaterThan(t *testing.T) { 54 | rl := finch.RunLevel{ 55 | Stage: 1, 56 | ExecGroup: 1, 57 | ClientGroup: 1, 58 | Client: 1, 59 | Trx: 1, 60 | Query: 1, 61 | } 62 | 63 | prev := rl 64 | 65 | if rl.GreaterThan(prev, finch.SCOPE_STATEMENT) != false { 66 | t.Errorf("no change but got true, expecte false") 67 | } 68 | if rl.GreaterThan(prev, finch.SCOPE_CLIENT) != false { 69 | t.Errorf("no change but got true, expecte false") 70 | } 71 | 72 | rl.Client += 1 // increase run level 73 | if rl.GreaterThan(prev, finch.SCOPE_STATEMENT) != true { 74 | t.Errorf("Client changed but got false for STATEMENT") 75 | } 76 | if rl.GreaterThan(prev, finch.SCOPE_TRX) != true { 77 | t.Errorf("Client changed but got false for TRX") 78 | } 79 | if rl.GreaterThan(prev, finch.SCOPE_ITER) != true { 80 | t.Errorf("Client changed but got false for ITER") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/microformats/opengraph.html: -------------------------------------------------------------------------------- 1 | {{ $isPage := or (and (ne .Type "posts") (in "section page" .Kind )) (and (eq .Type "posts") (eq .Kind "page")) }} 2 | 3 | {{- if ne .Kind "home" }} 4 | 8 | {{- end }} 9 | {{- with .Site.Title }} 10 | 11 | {{- end }} 12 | {{- with partial "utils/featured" . }} 13 | 14 | {{- end }} 15 | {{- with partial "utils/description" . }} 16 | 17 | {{- end }} 18 | 19 | 20 | {{- with .Params.audio }} 21 | 22 | {{- end }} 23 | {{- with .Params.locale }} 24 | 25 | {{- end }} 26 | {{- with .Params.videos }} 27 | {{- range . }} 28 | 29 | {{- end }} 30 | {{- end }} 31 | 32 | {{- /* If it is part of a series, link to related articles */}} 33 | {{- if .Site.Taxonomies.series }} 34 | {{- $permalink := .Permalink -}} 35 | {{- $siteSeries := .Site.Taxonomies.series -}} 36 | {{- with .Params.series }} 37 | {{- range $name := . }} 38 | {{- $series := index $siteSeries ($name | urlize) }} 39 | {{- range $page := first 6 $series.Pages }} 40 | {{- if ne $page.Permalink $permalink }} 41 | 42 | {{- end }} 43 | {{- end }} 44 | {{- end }} 45 | {{- end }} 46 | {{- end }} 47 | 48 | {{ if $isPage -}} 49 | {{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} 50 | 51 | {{- with .PublishDate }} 52 | 56 | {{- end }} 57 | {{- with .Lastmod }} 58 | 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /data/id_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package data_test 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/go-test/deep" 9 | 10 | "github.com/square/finch" 11 | "github.com/square/finch/data" 12 | ) 13 | 14 | func TestXid_TrxScope(t *testing.T) { 15 | g := data.NewScopedGenerator( 16 | data.Id{ 17 | Scope: finch.SCOPE_TRX, 18 | Type: "xid", 19 | DataKey: "@d", 20 | }, 21 | data.NewXid()) 22 | 23 | r := data.RunCount{} 24 | r[data.TRX] = 1 25 | 26 | v1 := g.Values(r) 27 | v2 := g.Values(r) 28 | 29 | if len(v1) != 1 || len(v2) != 1 { 30 | t.Fatalf("%d and %d values, expected 1 value from each call to Values()", len(v1), len(v2)) 31 | } 32 | 33 | if v1[0].(string) != v2[0].(string) { 34 | t.Errorf("different values for same trx, expect same: %s != %s", v1, v2) 35 | } 36 | 37 | // Next trx should cause new value 38 | r[data.TRX] += 1 39 | v3 := g.Values(r) 40 | v4 := g.Values(r) 41 | 42 | if len(v3) != 1 || len(v4) != 1 { 43 | t.Fatalf("%d and %d values, expected 1 value from each call to Values()", len(v3), len(v4)) 44 | } 45 | 46 | if v3[0].(string) != v4[0].(string) { 47 | t.Errorf("different values for same trx, expect same: %s != %s", v3, v4) 48 | } 49 | 50 | if v3[0].(string) == v1[0].(string) { 51 | t.Errorf("trx 2 values == trx 1 values, expected different values: %s == %s", v3[0].(string), v1[0].(string)) 52 | } 53 | } 54 | 55 | func TestClientId(t *testing.T) { 56 | rc := data.RunCount{ 57 | 1, 1, 1, 1, // couters 58 | 5, 6, 7, 8, // client, cg, eg, stage 59 | } 60 | 61 | // With default (no params): returns just client id (5) 62 | g, err := data.NewClientId(nil) 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | got := g.Values(rc) 68 | expect := []interface{}{uint(5)} 69 | if diff := deep.Equal(got, expect); diff != nil { 70 | t.Error(diff) 71 | } 72 | 73 | n, _ := g.Format() 74 | if n != 1 { 75 | t.Errorf("Format return n=%d, expected 1", n) 76 | } 77 | 78 | // With all 3 ids: client, client group, exec group 79 | g, err = data.NewClientId(map[string]string{"ids": "client,client-group,exec-group"}) 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | 84 | got = g.Values(rc) 85 | expect = []interface{}{uint(5), uint(6), uint(7)} 86 | if diff := deep.Equal(got, expect); diff != nil { 87 | t.Error(diff) 88 | } 89 | 90 | n, _ = g.Format() 91 | if n != 3 { 92 | t.Errorf("Format return n=%d, expected 3", n) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /stats/remote.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package stats 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "log" 9 | "time" 10 | 11 | "github.com/square/finch" 12 | "github.com/square/finch/proto" 13 | ) 14 | 15 | // Server is a Reporter that sends stats to a remote compute instance (--server). 16 | // When running as a client, Finch uses and configures this reporter automatically 17 | // in compute/Remote.Boot. 18 | type Server struct { 19 | server string // for logging 20 | client *proto.Client 21 | statsChan chan Instance 22 | stopChan chan struct{} 23 | doneChan chan struct{} 24 | } 25 | 26 | var _ Reporter = Server{} 27 | 28 | func NewServer(opts map[string]string) (Server, error) { 29 | r := Server{ 30 | server: opts["server"], // for logging 31 | client: proto.NewClient(opts["client"], opts["server"]), 32 | statsChan: make(chan Instance, 5), 33 | 34 | stopChan: make(chan struct{}), 35 | doneChan: make(chan struct{}), 36 | } 37 | r.client.StageId = opts["stage-id"] // from compute/client.run 38 | go r.report() 39 | return r, nil 40 | } 41 | 42 | func (r Server) Report(from []Instance) { 43 | if len(from) != 1 { 44 | panic(fmt.Sprintf("stats/Server.Report passed %d stats, expected 1", len(from))) 45 | } 46 | 47 | // The Collector calls this func at the configured frequency 48 | // (config.stats.freq), and then we queue the stats via a channel 49 | // to the report() goroutine that sends them. Async sending with 50 | // the chan/goroutine allows us to handle intermittent network issues, 51 | // i.e. don't block in this func, else it'll block Collector and 52 | // mess up the timing of collecting the stats. 53 | select { 54 | case r.statsChan <- from[0]: 55 | default: 56 | log.Printf("Stats dropped because remote is not responding: %+v", from[0]) 57 | } 58 | } 59 | 60 | func (r Server) Stop() { 61 | finch.Debug("stopping") 62 | close(r.statsChan) 63 | select { 64 | case <-r.doneChan: 65 | finch.Debug("remote stats done") 66 | case <-time.After(5 * time.Second): 67 | log.Println("Timeout sending last stats") 68 | } 69 | } 70 | 71 | func (r Server) report() { 72 | defer close(r.doneChan) 73 | for s := range r.statsChan { 74 | err := r.client.Send(context.Background(), "/stats", s, proto.R{300 * time.Millisecond, 10 * time.Millisecond, 3}) 75 | if err != nil { 76 | log.Printf("Failed to send stats: %s\n%+v\n", err, s) 77 | continue 78 | } 79 | finch.Debug("sent stats to %s", r.server) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/brand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 43 | 47 | 51 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /.github/workflows/hugo.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Hugo site to GitHub Pages 2 | name: Deploy Hugo site to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | # Default to bash 25 | defaults: 26 | run: 27 | shell: bash 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | runs-on: ubuntu-latest 33 | env: 34 | HUGO_VERSION: 0.111.3 35 | steps: 36 | - name: Install Hugo CLI 37 | run: | 38 | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ 39 | && sudo dpkg -i ${{ runner.temp }}/hugo.deb 40 | - name: Install Dart Sass Embedded 41 | run: sudo snap install dart-sass-embedded 42 | - name: Checkout 43 | uses: actions/checkout@v3 44 | with: 45 | submodules: recursive 46 | - name: Setup Pages 47 | id: pages 48 | uses: actions/configure-pages@v3 49 | - name: Install Node.js dependencies 50 | run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" 51 | - name: Build with Hugo 52 | env: 53 | # For maximum backward compatibility with Hugo modules 54 | HUGO_ENVIRONMENT: production 55 | HUGO_ENV: production 56 | working-directory: ./docs 57 | run: | 58 | hugo \ 59 | --minify \ 60 | --baseURL "${{ steps.pages.outputs.base_url }}/" 61 | - name: Upload artifact 62 | uses: actions/upload-pages-artifact@v1 63 | with: 64 | path: ./docs/public 65 | 66 | # Deployment job 67 | deploy: 68 | environment: 69 | name: github-pages 70 | url: ${{ steps.deployment.outputs.page_url }} 71 | runs-on: ubuntu-latest 72 | needs: build 73 | steps: 74 | - name: Deploy to GitHub Pages 75 | id: deployment 76 | uses: actions/deploy-pages@v2 77 | -------------------------------------------------------------------------------- /docs/content/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 0 3 | --- 4 | 5 | # Finch 6 | 7 | Finch is a MySQL benchmark tool. 8 | Like most benchmark tools, it executes SQL statements, measures response time, and reports various statistics (QPS, min, max, P95). 9 | But _unlike_ most benchmark tools, Finch was developed for software engineers and modern infrastructures. 10 | 11 | ## 💡 Declarative 12 | 13 | Write benchmarks in SQL, not a scripting language. 14 | 15 | Let's say you're a developer who wants to know how the following transaction will perform on your database: 16 | 17 | ```sql 18 | BEGIN 19 | 20 | SELECT id FROM t WHERE user=? 21 | 22 | UPDATE t SET n=n+1 WHERE id=? 23 | 24 | COMMIT 25 | ``` 26 | 27 | That's how you write the benchmark with Finch: with SQL. 28 | This allows developers (and DBAs) to write benchmarks easily—without knowing an additional scripting language or programming API. 29 | 30 | ## 💡 Workload Orchestration 31 | 32 | Normal benchmark tools execute all transactions, in all clients, all at once. 33 | 34 | ![Normal workload execution](/finch/img/normal_benchmark_workload.svg) 35 | 36 | But real workloads (the combination of queries, data, and access patterns) can be far more complex. 37 | Finch lets you orchestrate complex workloads. 38 | 39 | ![Finch workload shaping](/finch/img/finch_benchmark_workload.svg) 40 | 41 | In the diagram above, clients in client group 1 execute statements in transaction 3 while, concurrently, clients in client group 2 execute statements in transaction 2. 42 | Together, these client groups form execution group 1. 43 | When execution group 1 finishes, execution group 2 begins. 44 | Clients in client group 3 execute statements in transactions 2 and 1. 45 | 46 | Whether your benchmark workload is simple or complex, Finch can execute it. 47 | 48 | ## 💡 Distributed Compute 49 | 50 | A single Finch server can coordinate multiple Finch clients running on separate compute instances. 51 | 52 | ![Finch Distributed Compute](/finch/img/finch_compute.svg) 53 | 54 | This allows you to hammer the database like the application: from any number of compute instances. 55 | It's a benchmarking bot network—wield the power responsibly. 56 | 57 | ## 💡 Plug-in Stats 58 | 59 | Finch stats are reported through plugins. 60 | Don't like CSV? 61 | Write a little Go plugin and dump the Finch stats anywhere, anyway you want. 62 | Finch doesn't judge; it just works hard. 63 | 64 | --- 65 | 66 | {{< hint type=tip title="Read Next" >}} 67 | [Start Here]({{< relref "intro/start-here" >}}) to learn Finch by writing quick and simple benchmarks. 68 | {{< /hint >}} 69 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/static/favicon/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 43 | 47 | 51 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /docs/content/operate/client-server.md: -------------------------------------------------------------------------------- 1 | --- 2 | weight: 2 3 | title: "Client/Server" 4 | --- 5 | 6 | ## Client 7 | 8 | Specify [`--client ADDR`]({{< relref "operate/command-line#--client" >}}) to run Finch as a client connected to the server at `ADDR`. 9 | 10 | A client ignores other [command line options]({{< relref "operate/command-line#command-line-options" >}}) and automatically receives stage and trx files from the server. 11 | 12 | The client runs only once. 13 | This is largely due to https://bugs.mysql.com/bug.php?id=110941: MySQL doesn't properly terminate clients/connections in some cases, especially when the client aborts the connection, which is what the Go MySQL driver does on context cancellation. 14 | 15 | ## Server 16 | 17 | Specify [`--server ADDR`]({{< relref "operate/command-line#--server" >}}) to run Finch as a server that listens on ADDR[:PORT} for remote compute instances. 18 | 19 | The recommended client-server startup sequence is server then clients: start the server, then start the clients. 20 | 21 | The server is counted as one compute instance called "local". 22 | Set [`stage.compute.disable-local`]({{< relref "syntax/stage-file#disable-local" >}}) to disable. 23 | 24 | {{< hint type=note >}} 25 | A standalone instance of Finch is a pseduo-server that doesn't bind to an interface and runs only locally. 26 | As a result, [`--debug`]({{< relref "operate/command-line#--debug" >}}) prints server info even when `--server` is not specififed. 27 | {{< /hint >}} 28 | 29 | ## Protocol 30 | 31 | The client-server protocol is initiated by clients over a standard HTTP port. 32 | The server needs to allow incoming HTTP on that port (default 33075). 33 | 34 | {{< mermaid class="text-center" >}} 35 | sequenceDiagram 36 | autonumber 37 | 38 | activate client 39 | client->>server: GET /boot 40 | server-->>client: return stage files 41 | 42 | loop Every trx file 43 | client->>server: GET /file?trx=N 44 | server-->>client: return trx file N 45 | end 46 | 47 | client->>server: POST /boot 48 | server-->>client: ack 49 | 50 | client->>server: GET /run 51 | deactivate client 52 | Note over client: Client waits for server 53 | Note over server: Server waits for stage.compute.instances 54 | 55 | server-->>client: ack 56 | 57 | activate client 58 | Note left of client: Client runs stages 59 | loop While running 60 | client->>server: POST /stats 61 | server-->>client: ack 62 | end 63 | deactivate client 64 | 65 | Note left of client: Client done running 66 | client->>server: POST /run 67 | server-->>client: ack 68 | {{< /mermaid >}} 69 | -------------------------------------------------------------------------------- /data/id.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Block, Inc. 2 | 3 | package data 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/rs/xid" 10 | 11 | "github.com/square/finch" 12 | ) 13 | 14 | // Xid implments the xid data generator. 15 | type Xid struct { 16 | val string 17 | } 18 | 19 | var _ Generator = &Xid{} 20 | 21 | func NewXid() *Xid { 22 | return &Xid{} 23 | } 24 | 25 | func (g *Xid) Name() string { return "xid" } 26 | func (g *Xid) Format() (uint, string) { return 1, "'%s'" } 27 | func (g *Xid) Scan(any interface{}) error { return nil } 28 | 29 | func (g *Xid) Copy() Generator { 30 | return NewXid() 31 | } 32 | 33 | func (g *Xid) Values(c RunCount) []interface{} { 34 | return []interface{}{xid.New().String()} 35 | } 36 | 37 | // -------------------------------------------------------------------------- 38 | 39 | // ClientId implments the client-id data generator. 40 | type ClientId struct { 41 | ids []byte 42 | } 43 | 44 | var _ Generator = &ClientId{} 45 | 46 | func NewClientId(params map[string]string) (*ClientId, error) { 47 | ids := []byte{} 48 | if len(params) == 0 { 49 | ids = append(ids, CLIENT) // just client ID by default 50 | } else { 51 | csvIds := params["ids"] 52 | if csvIds != "" { 53 | runlevels := strings.Split(csvIds, ",") 54 | for _, r := range runlevels { 55 | switch r { 56 | case finch.SCOPE_STATEMENT: 57 | ids = append(ids, STATEMENT) 58 | case finch.SCOPE_TRX: 59 | ids = append(ids, TRX) 60 | case finch.SCOPE_ITER: 61 | ids = append(ids, ITER) 62 | case "conn": 63 | ids = append(ids, CONN) 64 | case finch.SCOPE_CLIENT: 65 | ids = append(ids, CLIENT) 66 | case finch.SCOPE_CLIENT_GROUP: 67 | ids = append(ids, CLIENT_GROUP) 68 | case finch.SCOPE_EXEC_GROUP: 69 | ids = append(ids, EXEC_GROUP) 70 | default: 71 | return nil, fmt.Errorf("invalid scope: %s", r) 72 | } 73 | } 74 | } 75 | } 76 | return &ClientId{ids: ids}, nil 77 | } 78 | 79 | func (g *ClientId) Name() string { return "client-id" } 80 | func (g *ClientId) Format() (uint, string) { return uint(len(g.ids)), "%d" } 81 | func (g *ClientId) Scan(any interface{}) error { return nil } 82 | func (g *ClientId) Copy() Generator { return &ClientId{ids: g.ids} } 83 | 84 | func (g *ClientId) Values(rc RunCount) []interface{} { 85 | // This data generator can be shared (e.g. client-group scope), so the 86 | // caller can be a different client each time, so don't save the last value 87 | // and don't return something like g.val (a struct field) because Go slices 88 | // are pointers, so each call would overwrite the return value of the previous. 89 | val := make([]interface{}, len(g.ids)) // a new slice each call 90 | for i := range g.ids { 91 | val[i] = rc[g.ids[i]] 92 | } 93 | return val 94 | } 95 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/microformats/schema.html: -------------------------------------------------------------------------------- 1 | {{ $isPage := or (and (ne .Type "posts") (in "section page" .Kind )) (and (eq .Type "posts") (eq .Kind "page")) }} 2 | {{- if eq .Kind "home" }} 3 | 21 | {{- else if $isPage }} 22 | 70 | {{- end }} 71 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/menu-nextprev.html: -------------------------------------------------------------------------------- 1 | {{ $current := . }} 2 | {{ $site := .Site }} 3 | {{ $current.Scratch.Set "prev" false }} 4 | {{ $current.Scratch.Set "getNext" false }} 5 | 6 | {{ $current.Scratch.Set "nextPage" false }} 7 | {{ $current.Scratch.Set "prevPage" false }} 8 | 9 | {{ template "menu_nextprev" dict "sect" $.Site.Data.menu.main.main "current" $current "site" $site }} 10 | 11 | {{ define "menu_nextprev" }} 12 | {{ $current := .current }} 13 | {{ $site := .site }} 14 | 15 | {{ range sort (default (seq 0) .sect) "weight" }} 16 | {{ $current.Scratch.Set "current" $current }} 17 | {{ $current.Scratch.Set "site" $site }} 18 | 19 | {{ $ref := default false .ref }} 20 | {{ if $ref }} 21 | {{ $site := $current.Scratch.Get "site" }} 22 | {{ $this := $site.GetPage .ref }} 23 | {{ $current := $current.Scratch.Get "current" }} 24 | 25 | {{ if reflect.IsMap .name }} 26 | {{ $current.Scratch.Set "refName" (index .name $site.Language.Lang) }} 27 | {{ else }} 28 | {{ $current.Scratch.Set "refName" .name }} 29 | {{ end }} 30 | {{ $name := $current.Scratch.Get "refName" }} 31 | 32 | {{ if $current.Scratch.Get "getNext" }} 33 | {{ $current.Scratch.Set "nextPage" (dict "name" $name "this" $this) }} 34 | {{ $current.Scratch.Set "getNext" false }} 35 | {{ end }} 36 | 37 | {{ if eq $current $this }} 38 | {{ $current.Scratch.Set "prevPage" ($current.Scratch.Get "prev") }} 39 | {{ $current.Scratch.Set "getNext" true }} 40 | {{ end }} 41 | 42 | {{ $current.Scratch.Set "prev" (dict "name" $name "this" $this) }} 43 | {{ end }} 44 | 45 | {{ $sub := default false .sub }} 46 | {{ if $sub }} 47 | {{ template "menu_nextprev" dict "sect" $sub "current" ($current.Scratch.Get "current") "site" ($current.Scratch.Get "site") }} 48 | {{ end }} 49 | {{ end }} 50 | {{ end }} 51 | 52 | {{ $showPrevNext := (and (default true .Site.Params.GeekdocNextPrev) .Site.Params.GeekdocMenuBundle) }} 53 | {{ if $showPrevNext }} 54 | 55 | {{ with ($current.Scratch.Get "prevPage") }} 56 | 61 | gdoc_arrow_left_alt 62 | {{ .name }} 63 | 64 | {{ end }} 65 | 66 | 67 | {{ with ($current.Scratch.Get "nextPage") }} 68 | 73 | {{ .name }} 74 | gdoc_arrow_right_alt 75 | 76 | {{ end }} 77 | 78 | {{ end }} 79 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/page-header.html: -------------------------------------------------------------------------------- 1 | {{ $geekdocRepo := default (default false .Site.Params.GeekdocRepo) .Page.Params.GeekdocRepo }} 2 | {{ $geekdocEditPath := default (default false .Site.Params.GeekdocEditPath) .Page.Params.GeekdocEditPath }} 3 | {{ if .File }} 4 | {{ $.Scratch.Set "geekdocFilePath" (default (path.Join (default "content" .Site.Params.contentDir) .File.Path) .Page.Params.GeekdocFilePath) }} 5 | {{ else }} 6 | {{ $.Scratch.Set "geekdocFilePath" false }} 7 | {{ end }} 8 | 9 | {{ define "breadcrumb" }} 10 | {{ $parent := .page.Parent }} 11 | {{ if $parent }} 12 | {{ $name := (partial "utils/title" $parent) }} 13 | {{ $position := (sub .position 1) }} 14 | {{ $value := (printf "
  • %s
  • /
  • %s" $parent.RelPermalink $parent.RelPermalink $name $position .value) }} 15 | {{ template "breadcrumb" dict "page" $parent "value" $value "position" $position }} 16 | {{ else }} 17 | {{ .value | safeHTML }} 18 | {{ end }} 19 | {{ end }} 20 | 21 | {{ $showBreadcrumb := (and (default true .Page.Params.GeekdocBreadcrumb) (default true .Site.Params.GeekdocBreadcrumb)) }} 22 | {{ $showEdit := (and ($.Scratch.Get "geekdocFilePath") $geekdocRepo $geekdocEditPath) }} 23 |
    34 | {{ if $showBreadcrumb }} 35 |
    36 | 37 | 43 |
    44 | {{ end }} 45 | {{ if $showEdit }} 46 |
    47 | 48 | 49 | 52 | {{ i18n "edit_page" }} 53 | 54 | 55 |
    56 | {{ end }} 57 |
    58 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/shortcodes/img.html: -------------------------------------------------------------------------------- 1 | {{- $source := ($.Page.Resources.ByType "image").GetMatch (printf "%s" (.Get "name")) }} 2 | {{- $customAlt := .Get "alt" }} 3 | {{- $customSize := .Get "size" | lower }} 4 | {{- $lazyLoad := default (default true $.Site.Params.GeekdocImageLazyLoading) (.Get "lazy") }} 5 | {{- $data := newScratch }} 6 | 7 | {{- with $source }} 8 | {{- $caption := default .Title $customAlt }} 9 | {{- $isSVG := (eq .MediaType.SubType "svg") }} 10 | 11 | {{- $origin := .Permalink }} 12 | {{- if $isSVG }} 13 | {{- $data.SetInMap "size" "profile" "180" }} 14 | {{- $data.SetInMap "size" "tiny" "320" }} 15 | {{- $data.SetInMap "size" "small" "600" }} 16 | {{- $data.SetInMap "size" "medium" "1200" }} 17 | {{- $data.SetInMap "size" "large" "1800" }} 18 | {{- else }} 19 | {{- $data.SetInMap "size" "profile" (.Fill "180x180 Center").Permalink }} 20 | {{- $data.SetInMap "size" "tiny" (.Resize "320x").Permalink }} 21 | {{- $data.SetInMap "size" "small" (.Resize "600x").Permalink }} 22 | {{- $data.SetInMap "size" "medium" (.Resize "1200x").Permalink }} 23 | {{- $data.SetInMap "size" "large" (.Resize "1800x").Permalink }} 24 | {{- end }} 25 | 26 | 27 |
    28 |
    32 | 33 | 34 | {{- $size := $data.Get "size" }} 35 | {{- if not $isSVG }} 36 | 43 | {{- end }} 44 | {{ $caption }} 57 | 58 | 59 | {{- if not (eq $customSize "profile") }} 60 | {{- with $caption }} 61 |
    62 | {{ . }} 63 | {{- with $source.Params.credits }} 64 | {{ printf " (%s)" . | $.Page.RenderString }} 65 | {{- end }} 66 |
    67 | {{- end }} 68 | {{- end }} 69 |
    70 |
    71 | {{- end }} 72 | -------------------------------------------------------------------------------- /dbconn/mycnf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package dbconn 4 | 5 | import ( 6 | "strings" 7 | 8 | "github.com/go-ini/ini" 9 | 10 | "github.com/square/finch" 11 | "github.com/square/finch/config" 12 | ) 13 | 14 | // ParseMyCnf parses a MySQL my.cnf file. It only reads the "[client]" section, 15 | // same as the mysql CLI. 16 | func ParseMyCnf(file string) (config.MySQL, error) { 17 | opts := ini.LoadOptions{AllowBooleanKeys: true} 18 | mycnf, err := ini.LoadSources(opts, file) 19 | if err != nil { 20 | return config.MySQL{}, err 21 | } 22 | 23 | cfg := config.MySQL{ 24 | Username: mycnf.Section("client").Key("user").String(), 25 | Password: mycnf.Section("client").Key("password").String(), 26 | Hostname: mycnf.Section("client").Key("host").String(), 27 | Socket: mycnf.Section("client").Key("socket").String(), 28 | } 29 | 30 | port := mycnf.Section("client").Key("port").String() 31 | if port != "" { 32 | cfg.Hostname += ":" + port 33 | } 34 | 35 | // Translate MySQL ssl-* vars to config.TLS. The vars don't line up 36 | // perfectly because MySQL has several levels of TLS verification: 37 | // https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode 38 | // But Go tls.Config (which is derived from config.TLS) has only two 39 | // options: specify tls.Confg.ServerName _or_ .InsecureSkipVerify=true. 40 | mysqlTLS(file, mycnf, &cfg) 41 | 42 | finch.Debug("mycnf %s: %s %+v", file, cfg.Redacted()) 43 | return cfg, nil 44 | } 45 | 46 | func mysqlTLS(file string, mycnf *ini.File, cfg *config.MySQL) (tls config.TLS) { 47 | // USING IMPLICIT RETURN -----------------------------------^ 48 | 49 | tls.MySQLMode = strings.ToUpper(mycnf.Section("client").Key("ssl-mode").String()) 50 | if tls.MySQLMode == "" { 51 | tls.MySQLMode = "PREFERRED" // MySQL default 52 | } 53 | 54 | // Explicitly disabled = not TLS even if other vars set 55 | if tls.MySQLMode == "DISABLED" { 56 | finch.Debug("mycnf %s: ssl-mode=DISABLED", file) 57 | return 58 | } 59 | 60 | // As per the MySQL manual: 61 | // "Connections over Unix socket files are not encrypted with a mode of PREFERRED. 62 | // To enforce encryption for Unix socket-file connections, use a mode of REQUIRED or stricter. 63 | if cfg.Socket != "" && tls.MySQLMode == "PREFERRED" { 64 | finch.Debug("mycnf %s: ignoring TLS on socket %s", file, cfg.Socket) 65 | return 66 | } 67 | 68 | // Not TLS unless at least 1 of the 3 files is set (no validation yet) 69 | tls.CA = mycnf.Section("client").Key("ssl-ca").String() 70 | tls.Cert = mycnf.Section("client").Key("ssl-cert").String() 71 | tls.Key = mycnf.Section("client").Key("ssl-key").String() 72 | if !tls.Set() { 73 | finch.Debug("mycnf %s: TLS not set", file) 74 | return 75 | } 76 | 77 | // Probably legit/normal MySQL TLS config: hostname + at least 1 file. 78 | // But it's unclear if, for example, PREFERRED = SkipVerify=true? 79 | return 80 | } 81 | -------------------------------------------------------------------------------- /config/config_test.go: -------------------------------------------------------------------------------- 1 | package config_test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/go-test/deep" 9 | 10 | "github.com/square/finch/config" 11 | ) 12 | 13 | func TestValidate_Stage(t *testing.T) { 14 | c := config.Stage{} 15 | err := c.Validate() 16 | if err == nil { 17 | t.Error("stage with disable=false and zero trx returned err=nil, expected validation error") 18 | } 19 | 20 | c.Disable = true 21 | err = c.Validate() 22 | if err != nil { 23 | t.Errorf("stage with disable=true and zero trx returned an error, experted err=nil: %v", err) 24 | } 25 | } 26 | 27 | func TestVars(t *testing.T) { 28 | params := map[string]string{ 29 | "foo": "bar", 30 | "n": "100", 31 | "a-b": "val", 32 | } 33 | 34 | home := os.Getenv("HOME") 35 | 36 | var tests = []struct { 37 | s, expect string 38 | numbers bool 39 | }{ 40 | // numbers=true (humanize numbers: 1k -> 1000) 41 | {"rows: 5", "rows: 5", true}, 42 | {"rows: $params.n", "rows: 100", true}, 43 | {"rows: ${params.n}", "rows: 100", true}, 44 | {`p: "${params.foo}"`, `p: "bar"`, true}, 45 | {`p: _${params.foo}_`, `p: _bar_`, true}, 46 | {`r: $params.a-b`, `r: val`, true}, 47 | {"key: $params.n $params.foo", "key: 100 bar", true}, 48 | {"home: $HOME", "home: " + home, true}, // env var 49 | {"rows: 1K", "rows: 1000", true}, 50 | {"rows: 1,000", "rows: 1000", true}, 51 | {"size: 1GiB", "size: 1073741824", true}, 52 | {"(1, 2, 'foo')", "(1, 2, 'foo')", true}, 53 | // numbers=false 54 | {"db.abd6b.us-east-1.rds.amazonaws.com", "db.abd6b.us-east-1.rds.amazonaws.com", false}, 55 | } 56 | for _, tt := range tests { 57 | t.Run(tt.s, func(t *testing.T) { 58 | got, err := config.Vars(tt.s, params, tt.numbers) 59 | if err != nil { 60 | t.Errorf("got an error, expected nil: %v", err) 61 | } 62 | if got != tt.expect { 63 | t.Errorf("got '%s', expected '%s'", got, tt.expect) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestLoadWithBase(t *testing.T) { 70 | stages, err := config.Load([]string{"../test/config/b1/stage.yaml"}, nil, "", "") 71 | if err != nil { 72 | t.Error(err) 73 | } 74 | if len(stages) == 0 { 75 | t.Fatalf("got 0 stages, expected 1") 76 | } 77 | fileName, _ := filepath.Abs("../test/config/b1/stage.yaml") 78 | expect := config.Stage{ 79 | N: 1, 80 | Name: "test", 81 | File: fileName, 82 | Compute: config.Compute{ 83 | Instances: "1", 84 | }, 85 | Params: map[string]string{ 86 | "foo": "test", 87 | }, 88 | Stats: config.Stats{ 89 | Freq: "0s", 90 | Report: map[string]map[string]string{ 91 | "stdout": map[string]string{ 92 | "each-instance": "true", 93 | }, 94 | }, 95 | }, 96 | Trx: []config.Trx{ 97 | { 98 | Name: "trx.sql", 99 | File: "trx.sql", 100 | }, 101 | }, 102 | } 103 | if diff := deep.Equal(stages[0], expect); diff != nil { 104 | t.Error(diff) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /limit/rate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package limit 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | gorate "golang.org/x/time/rate" 10 | 11 | "github.com/square/finch" 12 | ) 13 | 14 | type Rate interface { 15 | Adjust(byte) 16 | Current() (byte, string) 17 | Allow() <-chan bool 18 | Stop() 19 | } 20 | 21 | type rate struct { 22 | c chan bool 23 | n uint 24 | rl *gorate.Limiter 25 | stopChan chan struct{} 26 | } 27 | 28 | var _ Rate = &rate{} 29 | 30 | func NewRate(perSecond uint) Rate { 31 | if perSecond == 0 { 32 | return nil 33 | } 34 | finch.Debug("new rate: %d/s", perSecond) 35 | lm := &rate{ 36 | rl: gorate.NewLimiter(gorate.Limit(perSecond), 1), 37 | c: make(chan bool, 1), 38 | stopChan: make(chan struct{}), 39 | } 40 | go lm.run() 41 | return lm 42 | } 43 | 44 | func (lm *rate) Adjust(p byte) { 45 | } 46 | 47 | func (lm *rate) Current() (p byte, s string) { 48 | return 0, "" 49 | } 50 | 51 | func (lm *rate) Stop() { 52 | } 53 | 54 | func (lm *rate) Allow() <-chan bool { 55 | return lm.c 56 | } 57 | 58 | func (lm *rate) run() { 59 | var err error 60 | for { 61 | err = lm.rl.Wait(context.Background()) 62 | if err != nil { 63 | // burst limit exceeded? 64 | continue 65 | } 66 | select { 67 | case lm.c <- true: 68 | case <-lm.stopChan: 69 | return 70 | default: 71 | // dropped 72 | } 73 | } 74 | } 75 | 76 | // -------------------------------------------------------------------------- 77 | 78 | type and struct { 79 | c chan bool 80 | n uint 81 | a Rate 82 | b Rate 83 | } 84 | 85 | var _ Rate = &and{} 86 | 87 | // And makes a Rate limiter that allows execution when both a and b allow it. 88 | // This is used to combine QPS and TPS rate limits to keep clients at or below 89 | // both rates. 90 | func And(a, b Rate) Rate { 91 | if a == nil && b == nil { 92 | return nil 93 | } 94 | if a == nil && b != nil { 95 | return b 96 | } 97 | if a != nil && b == nil { 98 | return a 99 | } 100 | lm := &and{ 101 | a: a, 102 | b: b, 103 | c: make(chan bool, 1), 104 | } 105 | go lm.run() 106 | return lm 107 | } 108 | 109 | func (lm *and) Allow() <-chan bool { 110 | return lm.c 111 | } 112 | 113 | func (lm *and) N(_ uint) { 114 | } 115 | 116 | func (lm *and) Adjust(p byte) { 117 | lm.a.Adjust(p) 118 | lm.b.Adjust(p) 119 | } 120 | 121 | func (lm *and) Current() (p byte, s string) { 122 | p1, s1 := lm.a.Current() 123 | p2, s2 := lm.a.Current() 124 | if p1 != p2 { 125 | panic(fmt.Sprintf("lm.A %d != lm.B %d", p1, p2)) 126 | } 127 | return p1, s1 + " and " + s2 128 | } 129 | 130 | func (lm *and) Stop() { 131 | lm.a.Stop() 132 | lm.b.Stop() 133 | } 134 | 135 | func (lm *and) run() { 136 | a := false 137 | b := false 138 | for { 139 | select { 140 | case <-lm.a.Allow(): 141 | a = true 142 | case <-lm.b.Allow(): 143 | b = true 144 | } 145 | if a && b { 146 | select { 147 | case lm.c <- true: 148 | default: 149 | // dropped 150 | } 151 | a = false 152 | b = false 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /docs/themes/hugo-geekdoc/layouts/partials/site-header.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | {{ if .MenuEnabled }} 4 | 14 | {{ end }} 15 | 27 |
    28 | 29 | {{ if .Root.Site.Data.menu.extra.header }} 30 | {{ partial "menu-extra" (dict "current" .Root "source" .Root.Site.Data.menu.extra.header "target" "header") }} 31 | {{ end }} 32 | 33 | 34 | 35 | 36 | {{ i18n "button_toggle_dark" }} 37 | 38 | 39 | 40 | {{ i18n "button_toggle_dark" }} 41 | 42 | 43 | 44 | {{ i18n "button_toggle_dark" }} 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {{ i18n "button_homepage" }} 53 | 54 | 55 | 56 | 57 | 58 | {{ partial "language" .Root }} 59 | 60 | 61 | 62 | 68 | 69 | 70 | 76 |
    77 |
    78 |
    79 | -------------------------------------------------------------------------------- /proto/proto.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Block, Inc. 2 | 3 | package proto 4 | 5 | import ( 6 | "bytes" 7 | "context" 8 | "encoding/json" 9 | "errors" 10 | "io" 11 | "log" 12 | "net/http" 13 | "net/url" 14 | "strings" 15 | "time" 16 | 17 | "github.com/square/finch" 18 | ) 19 | 20 | var ErrFailed = errors.New("request failed after attempts, or context cancelled") 21 | 22 | type R struct { 23 | Timeout time.Duration 24 | Wait time.Duration 25 | Tries int 26 | } 27 | 28 | type Client struct { 29 | name string 30 | serverAddr string 31 | // -- 32 | client *http.Client 33 | StageId string 34 | PrintErrors bool 35 | } 36 | 37 | func NewClient(name, server string) *Client { 38 | return &Client{ 39 | name: name, 40 | serverAddr: server, 41 | // -- 42 | client: finch.MakeHTTPClient(), 43 | } 44 | } 45 | 46 | func (c *Client) Get(ctx context.Context, endpoint string, params [][]string, r R) (*http.Response, []byte, error) { 47 | return c.request(ctx, "GET", endpoint, params, nil, r) 48 | } 49 | 50 | func (c *Client) Send(ctx context.Context, endpoint string, data interface{}, r R) error { 51 | _, _, err := c.request(ctx, "POST", endpoint, nil, data, r) 52 | return err 53 | } 54 | 55 | func (c *Client) request(ctx context.Context, method string, endpoint string, params [][]string, data interface{}, r R) (*http.Response, []byte, error) { 56 | url := c.URL(endpoint, params) 57 | finch.Debug("%s %s", method, url) 58 | 59 | buf := new(bytes.Buffer) 60 | if data != nil { 61 | json.NewEncoder(buf).Encode(data) 62 | } 63 | 64 | var err error 65 | var body []byte 66 | var req *http.Request 67 | var resp *http.Response 68 | try := 0 69 | for r.Tries == -1 || try < r.Tries { 70 | try += 1 71 | ctxReq, cancelReq := context.WithTimeout(ctx, r.Timeout) 72 | req, _ = http.NewRequestWithContext(ctxReq, method, url, buf) 73 | resp, err = c.client.Do(req) 74 | cancelReq() 75 | if err != nil { 76 | goto RETRY 77 | } 78 | 79 | body, err = io.ReadAll(resp.Body) 80 | resp.Body.Close() 81 | if err != nil { 82 | return nil, nil, err 83 | } 84 | 85 | switch resp.StatusCode { 86 | case http.StatusOK: 87 | return resp, body, nil // success 88 | case http.StatusResetContent: 89 | return resp, nil, nil // reset 90 | default: 91 | goto RETRY 92 | } 93 | 94 | RETRY: 95 | if ctx.Err() != nil { 96 | return nil, nil, ctx.Err() 97 | } 98 | finch.Debug("%v", err) 99 | if c.PrintErrors && try%20 == 0 { 100 | log.Printf("Request error, retrying: %v", err) 101 | } 102 | time.Sleep(r.Wait) 103 | } 104 | return nil, nil, ErrFailed 105 | } 106 | 107 | func (c *Client) URL(path string, params [][]string) string { 108 | // Every request requires 'name=...' to tell server this client's name. 109 | // It's not a hostname, just a user-defined name for the remote compute instance. 110 | u := c.serverAddr + path + "?name=" + url.QueryEscape(c.name) 111 | n := len(params) + 1 112 | escaped := make([]string, len(params)+1) 113 | for i := range params { 114 | escaped[i] = params[i][0] + "=" + url.QueryEscape(params[i][1]) 115 | } 116 | escaped[n-1] = "stage-id=" + c.StageId 117 | u += "&" + strings.Join(escaped, "&") 118 | return u 119 | } 120 | --------------------------------------------------------------------------------