├── 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 |
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 `\s*\s* ` `` | safeHTML }}
4 | {{ $content = $content | replaceRE `(
)` ` ${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 |
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 |
4 | {{ default "Expand" (.Get 0) }}
5 | {{ default "↕" (.Get 1) }}
6 |
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 |
16 | {{ $tab.Name }}
17 |
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 |
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 |
5 |
13 | {{ i18n "pagination_page_state" $pag }}
14 |
22 |
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 |
9 |
10 |
11 |
12 | {{ $pageCount := len .Pages }}
13 |
14 |
15 | {{ i18n "posts_count" $pageCount }}
16 |
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 | 基于
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 |
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 |
4 |
9 |
12 |
24 |
25 |
30 |
31 | {{ end }}
32 | {{ partial "pagination.html" . }}
33 | {{ end }}
34 |
35 | {{ define "post-tag" }}
36 |
37 |
38 |
43 | {{ .name }}
44 |
45 |
46 |
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 |
4 |
9 |
10 |
13 |
14 |
26 |
27 |
32 |
33 | {{ end }}
34 | {{ partial "pagination.html" . }}
35 | {{ end }}
36 |
37 | {{ define "post-tag" }}
38 |
39 |
40 |
45 | {{ .name }}
46 |
47 |
48 |
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 | でビルドしています。
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 |
2 | {{ partial "search" . }}
3 |
4 |
5 |
6 | {{ i18n "nav_navigation" }}
7 | {{ if .Site.Params.GeekdocMenuBundle }}
8 | {{ partial "menu-bundle" (dict "current" . "source" .Site.Data.menu.main.main) }}
9 | {{ else }}
10 | {{ partial "menu-filetree" . }}
11 | {{ end }}
12 |
13 |
14 | {{ if and (in (slice "posts" "tags") .Section) (default false .Site.Params.GeekdocTagsToMenu) }}
15 |
35 | {{ end }}
36 |
37 |
38 |
39 | {{ if .Site.Data.menu.more.more }}
40 | {{ i18n "nav_more" }}
41 | {{ partial "menu-bundle" (dict "current" . "source" .Site.Data.menu.more.more) }}
42 | {{ end }}
43 |
44 |
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 |
13 | {{- range .sect.GroupBy "Weight" }}
14 | {{- range .ByTitle }}
15 | {{- if or (not .Params.GeekdocHidden) (not (default true .Params.GeekdocHiddenTocTree)) }}
16 |
17 | {{- if or .Content .Params.GeekdocFlatSection }}
18 |
19 |
20 | {{- partial "utils/title" . }}{{ with .Params.GeekdocDescription }}:{{ end }}
21 |
22 | {{- with .Params.GeekdocDescription }}{{ . }}{{ end }}
23 |
24 | {{- else -}}
25 |
26 | {{- partial "utils/title" . }}{{ with .Params.GeekdocDescription }}: {{ . }}{{ end }}
27 |
28 | {{- end -}}
29 |
30 | {{- $numberOfPages := (add (len .Pages) (len .Sections)) }}
31 | {{- if and (ne $numberOfPages 0) (not .Params.GeekdocFlatSection) }}
32 | {{- template "toc-tree" dict "sect" .Pages }}
33 | {{- end }}
34 |
35 | {{- end }}
36 | {{- end }}
37 | {{- end }}
38 |
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 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 |
4 |
5 | {{ if .Lastmod.After (.Date.AddDate 0 0 1) }}
6 | {{ i18n "posts_update_prefix" }}
7 | {{ end }}
8 | {{ .Lastmod.Format "Jan 2, 2006" }}
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ i18n "posts_read_time" .ReadingTime }}
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 |
40 |
45 | {{ .name }}
46 |
47 |
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 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 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í 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 |
40 | {{ partial "menu" . }}
41 |
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 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 | 
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 | 
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 | 
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 |
38 | {{ $position := sub (len (split .RelPermalink "/")) 1 }}
39 | {{ $name := (partial "utils/title" .) }}
40 | {{ $value := (printf "%s " $name $position ) }}
41 | {{ template "breadcrumb" dict "page" . "value" $value "position" $position }}
42 |
43 |
44 | {{ end }}
45 | {{ if $showEdit }}
46 |
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 |
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 |
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 |
--------------------------------------------------------------------------------