{"id":13490636,"url":"https://github.com/timescale/tsbs","last_synced_at":"2025-05-14T22:08:32.161Z","repository":{"id":40395520,"uuid":"144025618","full_name":"timescale/tsbs","owner":"timescale","description":"Time Series Benchmark Suite, a tool for comparing and evaluating databases for time series data","archived":false,"fork":false,"pushed_at":"2024-08-06T04:24:35.000Z","size":7650,"stargazers_count":1351,"open_issues_count":105,"forks_count":312,"subscribers_count":45,"default_branch":"master","last_synced_at":"2025-04-10T11:02:49.467Z","etag":null,"topics":["benchmarking","cassandra","influxdb","mongodb","time-series","timescaledb"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timescale.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-08T14:30:28.000Z","updated_at":"2025-04-09T05:03:45.000Z","dependencies_parsed_at":"2023-12-21T16:45:13.715Z","dependency_job_id":"717af5e2-afe7-4b27-9d3b-71fb2b8c6adc","html_url":"https://github.com/timescale/tsbs","commit_stats":{"total_commits":685,"total_committers":51,"mean_commits":"13.431372549019608","dds":0.6423357664233577,"last_synced_commit":"37fced794d56d3b33fbf40e55a0f1d3d78d05802"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Ftsbs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Ftsbs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Ftsbs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Ftsbs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timescale","download_url":"https://codeload.github.com/timescale/tsbs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254235700,"owners_count":22036964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmarking","cassandra","influxdb","mongodb","time-series","timescaledb"],"created_at":"2024-07-31T19:00:49.703Z","updated_at":"2025-05-14T22:08:27.149Z","avatar_url":"https://github.com/timescale.png","language":"Go","funding_links":[],"categories":["Go","Timeseries","数据库管理系统"],"sub_categories":["Databases","网络服务_其他"],"readme":"# Time Series Benchmark Suite (TSBS)\nThis repo contains code for benchmarking several time series databases,\nincluding TimescaleDB, MongoDB, InfluxDB, CrateDB and Cassandra.\nThis code is based on a fork of work initially made public by InfluxDB\nat https://github.com/influxdata/influxdb-comparisons.\n\nCurrent databases supported:\n\n+ Akumuli [(supplemental docs)](docs/akumuli.md)\n+ Cassandra [(supplemental docs)](docs/cassandra.md)\n+ ClickHouse [(supplemental docs)](docs/clickhouse.md)\n+ CrateDB [(supplemental docs)](docs/cratedb.md)\n+ InfluxDB [(supplemental docs)](docs/influx.md)\n+ MongoDB [(supplemental docs)](docs/mongo.md)\n+ QuestDB [(supplemental docs)](docs/questdb.md)\n+ SiriDB [(supplemental docs)](docs/siridb.md)\n+ TimescaleDB [(supplemental docs)](docs/timescaledb.md)\n+ Timestream [(supplemental docs)](docs/timestream.md)\n+ VictoriaMetrics [(supplemental docs)](docs/victoriametrics.md)\n\n## Overview\n\nThe **Time Series Benchmark Suite (TSBS)** is a collection of Go\nprograms that are used to generate datasets and then benchmark read\nand write performance of various databases. The intent is to make the\nTSBS extensible so that a variety of use cases (e.g., devops, IoT,\nfinance, etc.), query types, and databases can be included and benchmarked.\nTo this end we hope to help prospective database administrators find the\nbest database for their needs and their workloads. Further, if you\nare the developer of a time series database and want to include your\ndatabase in the TSBS, feel free to open a pull request to add it!\n\n## Current use cases\n\nCurrently, TSBS supports two use cases.\n\n### Dev ops\nA 'dev ops' use case, which comes in two forms. The full form is used to\ngenerate, insert, and measure data from 9 'systems' that could be monitored\nin a real world dev ops scenario (e.g., CPU, memory, disk, etc).\nTogether, these 9 systems generate 100 metrics per reading interval.\nThe alternate form focuses solely on CPU metrics for a simpler, more\nstreamlined use case. This use case generates 10 CPU metrics per reading.\n\nIn addition to metric readings, 'tags' (including the location\nof the host, its operating system, etc) are generated for each host\nwith readings in the dataset. Each unique set of tags identifies\none host in the dataset and the number of different hosts generated is\ndefined by the `scale` flag (see below).\n\n### Internet of Things (IoT)\nThe second use case is meant to simulate the data load in an IoT\nenvironment. This use case simulates data streaming from a set of trucks\nbelonging to a fictional trucking company. This use case simulates\ndiagnostic data and metrics from each truck, and introduces environmental\nfactors such as out-of-order data and batch ingestion (for trucks\nthat are offline for a period of time). It also tracks truck metadata\nand uses this to tie metrics and diagnostics together as part of the query\nset.  \n\nThe queries that are generated as part of this use case will cover both real\ntime truck status and analytics that will look at the time series data in\nan effort to be more predictive about truck behavior.  The scale factor with\nthis use case will be based on the number of trucks tracked.  \n\n---\n\nNot all databases implement all use cases. This table below shows which use\ncases are implemented for each database:\n\n|Database|Dev ops|IoT|\n|:---|:---:|:---:|\n|Akumuli|X¹||\n|Cassandra|X||\n|ClickHouse|X||\n|CrateDB|X||\n|InfluxDB|X|X|\n|MongoDB|X|\n|QuestDB|X|X\n|SiriDB|X|\n|TimescaleDB|X|X|\n|Timestream|X||\n|VictoriaMetrics|X²||\n\n¹ Does not support the `groupby-orderby-limit` query\n² Does not support the `groupby-orderby-limit`, `lastpoint`, `high-cpu-1`, `high-cpu-all` queries\n\n## What the TSBS tests\n\nTSBS is used to benchmark bulk load performance and\nquery execution performance. (It currently does not measure\nconcurrent insert and query performance, which is a future priority.)\nTo accomplish this in a fair way, the data to be inserted and the\nqueries to run are pre-generated and native Go clients are used\nwherever possible to connect to each database (e.g., `mgo` for MongoDB, \n`aws sdk` for Timestream).\n\nAlthough the data is randomly generated, TSBS data and queries are\nentirely deterministic. By supplying the same PRNG (pseudo-random number\ngenerator) seed to the generation programs, each database is loaded\nwith identical data and queried using identical queries.\n\n## Installation\n\nTSBS is a collection of Go programs (with some auxiliary bash and Python\nscripts). The easiest way to get and install the Go programs is to use\n`go get` and then `make all` to install all binaries:\n```bash\n# Fetch TSBS and its dependencies\n$ go get github.com/timescale/tsbs\n$ cd $GOPATH/src/github.com/timescale/tsbs\n$ make\n```\n\n## How to use TSBS\n\nUsing TSBS for benchmarking involves 3 phases: data and query\ngeneration, data loading/insertion, and query execution.\n\n### Data and query generation\n\nSo that benchmarking results are not affected by generating data or\nqueries on-the-fly, with TSBS you generate the data and queries you want\nto benchmark first, and then you can (re-)use it as input to the\nbenchmarking phases.\n\n#### Data generation\n\nVariables needed:\n1. a use case. E.g., `iot` (choose from `cpu-only`, `devops`, or `iot`)\n1. a PRNG seed for deterministic generation. E.g., `123`\n1. the number of devices / trucks to generate for. E.g., `4000`\n1. a start time for the data's timestamps. E.g., `2016-01-01T00:00:00Z`\n1. an end time. E.g., `2016-01-04T00:00:00Z`\n1. how much time should be between each reading per device, in seconds. E.g., `10s`\n1. and which database(s) you want to generate for. E.g., `timescaledb`\n (choose from `cassandra`, `clickhouse`, `cratedb`, `influx`, `mongo`, `questdb`, `siridb`,\n  `timescaledb` or `victoriametrics`)\n\nGiven the above steps you can now generate a dataset (or multiple\ndatasets, if you chose to generate for multiple databases) that can\nbe used to benchmark data loading of the database(s) chosen using\nthe `tsbs_generate_data` tool:\n```bash\n$ tsbs_generate_data --use-case=\"iot\" --seed=123 --scale=4000 \\\n    --timestamp-start=\"2016-01-01T00:00:00Z\" \\\n    --timestamp-end=\"2016-01-04T00:00:00Z\" \\\n    --log-interval=\"10s\" --format=\"timescaledb\" \\\n    | gzip \u003e /tmp/timescaledb-data.gz\n\n# Each additional database would be a separate call.\n```\n_Note: We pipe the output to gzip to reduce on-disk space. This also requires\nyou to pipe through gunzip when you run your tests._\n\nThe example above will generate a pseudo-CSV file that can be used to\nbulk load data into TimescaleDB. Each database has it's own format of how\nit stores the data to make it easiest for its corresponding loader to\nwrite data. The above configuration will generate just over 100M rows\n(1B metrics), which is usually a good starting point.\nIncreasing the time period by a day will add an additional ~33M rows\nso that, e.g., 30 days would yield a billion rows (10B metrics)\n\n##### IoT use case\n\nThe main difference between the `iot` use case and other use cases is that\nit generates data which can contain out-of-order, missing, or empty\nentries to better represent real-life scenarios associated to the use case.\nUsing a specified seed means that we can do this in a deterministic and\nreproducible way for multiple runs of data generation.\n\n#### Query generation\n\nVariables needed:\n1. the same use case, seed, # of devices, and start time as used in data generation\n1. an end time that is one second after the end time from data generation. E.g., for `2016-01-04T00:00:00Z` use `2016-01-04T00:00:01Z`\n1. the number of queries to generate. E.g., `1000`\n1. and the type of query you'd like to generate. E.g., `single-groupby-1-1-1` or `last-loc`\n\nFor the last step there are numerous queries to choose from, which are\nlisted in [Appendix I](#appendix-i-query-types). Additionally, the file\n`scripts/generate_queries.sh` contains a list of all of them as the\ndefault value for the environmental variable `QUERY_TYPES`. If you are\ngenerating more than one type of query, we recommend you use the\nhelper script.\n\nFor generating just one set of queries for a given type:\n```bash\n$ tsbs_generate_queries --use-case=\"iot\" --seed=123 --scale=4000 \\\n    --timestamp-start=\"2016-01-01T00:00:00Z\" \\\n    --timestamp-end=\"2016-01-04T00:00:01Z\" \\\n    --queries=1000 --query-type=\"breakdown-frequency\" --format=\"timescaledb\" \\\n    | gzip \u003e /tmp/timescaledb-queries-breakdown-frequency.gz\n```\n_Note: We pipe the output to gzip to reduce on-disk space. This also requires\nyou to pipe through gunzip when you run your tests._\n\nFor generating sets of queries for multiple types:\n```bash\n$ FORMATS=\"timescaledb\" SCALE=4000 SEED=123 \\\n    TS_START=\"2016-01-01T00:00:00Z\" \\\n    TS_END=\"2016-01-04T00:00:01Z\" \\\n    QUERIES=1000 QUERY_TYPES=\"last-loc low-fuel avg-load\" \\\n    BULK_DATA_DIR=\"/tmp/bulk_queries\" scripts/generate_queries.sh\n```\n\nA full list of query types can be found in\n[Appendix I](#appendix-i-query-types) at the end of this README.\n\n### Benchmarking insert/write performance\n\nTSBS has two ways to benchmark insert/write performance:\n* On the fly simulation and load with `tsbs_load`\n* Pre-generate data to a file and load it either with `tsbs_load` or the\ndb specific executables `tsbs_load_*`\n\n#### Using the unified `tsbs_load` executable\n\nThe `tsbs_load` executable can load data in any of the supported databases.\nIt can use a pregenerated data file as input, or simulate the data on the \nfly. \n\nYou first start by generating a config yaml file populated with the default\nvalues for each property with:\n```shell script\n$ tsbs_load config --target=\u003cdb-name\u003e --data-source=[FILE|SIMULATOR]\n```\nfor example, to generate an example for TimescaleDB, loading the data from file\n```shell script\n$ tsbs_load config --target=timescaledb --data-source=FILE\nWrote example config to: ./config.yaml\n```\n\nYou can then run tsbs_load with the generated config file with:\n```shell script\n$ tsbs_load load timescaledb --config=./config.yaml\n```\n\nFor more details on how to use tsbs_load check out the [supplemental docs](docs/tsbs_load.md)\n\n#### Using the database specific `tsbs_load_*` executables\n\nTSBS measures insert/write performance by taking the data generated in\nthe previous step and using it as input to a database-specific command\nline program. To the extent that insert programs can be shared, we have\nmade an effort to do that (e.g., the TimescaleDB loader can\nbe used with a regular PostgreSQL database if desired). Each loader does\nshare some common flags -- e.g., batch size (number of readings inserted\ntogether), workers (number of concurrently inserting clients), connection\ndetails (host \u0026 ports), etc -- but they also have database-specific tuning\nflags. To find the flags for a particular database, use the `-help` flag\n(e.g., `tsbs_load_timescaledb -help`).\n\nHere's an example of loading data to a remote timescaledb instance with SSL\nrequired, with a gzipped data set as created in the instructions above:\n\n```bash\ncat /tmp/timescaledb-data.gz | gunzip | tsbs_load_timescaledb \\\n--postgres=\"sslmode=require\" --host=\"my.tsdb.host\" --port=5432 --pass=\"password\" \\\n--user=\"benchmarkuser\" --admin-db-name=defaultdb --workers=8  \\\n--in-table-partition-tag=true --chunk-time=8h --write-profile= \\\n--field-index-count=1 --do-create-db=true --force-text-format=false \\\n--do-abort-on-exist=false\n```\n\nFor simpler testing, especially locally, we also supply\n`scripts/load/load_\u003cdatabase\u003e.sh` for convenience with many of the flags set\nto a reasonable default for some of the databases.\nSo for loading into TimescaleDB, ensure that TimescaleDB is running and\nthen use:\n```bash\n# Will insert using 2 clients, batch sizes of 10k, from a file\n# named `timescaledb-data.gz` in directory `/tmp`\n$ NUM_WORKERS=2 BATCH_SIZE=10000 BULK_DATA_DIR=/tmp \\\n    scripts/load/load_timescaledb.sh\n```\n\nThis will create a new database called `benchmark` where the data is\nstored. It **will overwrite** the database if it exists; if you don't\nwant that to happen, supply a different `DATABASE_NAME` to the above\ncommand.\n\nExample for writing to remote host using `load_timescaledb.sh`:\n```bash\n# Will insert using 2 clients, batch sizes of 10k, from a file\n# named `timescaledb-data.gz` in directory `/tmp`\n$ NUM_WORKERS=2 BATCH_SIZE=10000 BULK_DATA_DIR=/tmp DATABASE_HOST=remotehostname\nDATABASE_USER=user DATABASE \\\n    scripts/load/load_timescaledb.sh\n```\n\n---\n\nBy default, statistics about the load performance are printed every 10s,\nand when the full dataset is loaded the looks like this:\n```text\ntime,per. metric/s,metric total,overall metric/s,per. row/s,row total,overall row/s\n# ...\n1518741528,914996.143291,9.652000E+08,1096817.886674,91499.614329,9.652000E+07,109681.788667\n1518741548,1345006.018902,9.921000E+08,1102333.152918,134500.601890,9.921000E+07,110233.315292\n1518741568,1149999.844750,1.015100E+09,1103369.385320,114999.984475,1.015100E+08,110336.938532\n\nSummary:\nloaded 1036800000 metrics in 936.525765sec with 8 workers (mean rate 1107070.449780/sec)\nloaded 103680000 rows in 936.525765sec with 8 workers (mean rate 110707.044978/sec)\n```\n\nAll but the last two lines contain the data in CSV format, with column names in the header. Those column names correspond to:\n* timestamp,\n* metrics per second in the period,\n* total metrics inserted,\n* overall metrics per second,\n* rows per second in the period,\n* total number of rows,\n* overall rows per second.\n\nFor databases, like Cassandra, that do not use rows when inserting,\nthe last three values are always empty (indicated with a `-`).\n\nThe last two lines are a summary of how many metrics (and rows where\napplicable) were inserted, the wall time it took, and the average rate\nof insertion.\n\n### Benchmarking query execution performance\n\nTo measure query execution performance in TSBS, you first need to load\nthe data using the previous section and generate the queries as\ndescribed earlier. Once the data is loaded and the queries are generated,\njust use the corresponding `tsbs_run_queries_` binary for the database\nbeing tested:\n```bash\n$ cat /tmp/queries/timescaledb-cpu-max-all-eight-hosts-queries.gz | \\\n    gunzip | tsbs_run_queries_timescaledb --workers=8 \\\n        --postgres=\"host=localhost user=postgres sslmode=disable\"\n```\n\nYou can change the value of the `--workers` flag to\ncontrol the level of parallel queries run at the same time. The\nresulting output will look similar to this:\n```text\nrun complete after 1000 queries with 8 workers:\nTimescaleDB max cpu all fields, rand    8 hosts, rand 12hr by 1h:\nmin:    51.97ms, med:   757.55, mean:  2527.98ms, max: 28188.20ms, stddev:  2843.35ms, sum: 5056.0sec, count: 2000\nall queries                                                     :\nmin:    51.97ms, med:   757.55, mean:  2527.98ms, max: 28188.20ms, stddev:  2843.35ms, sum: 5056.0sec, count: 2000\nwall clock time: 633.936415sec\n```\n\nThe output gives you the description of the query and multiple groupings\nof measurements (which may vary depending on the database).\n\n---\n\nFor easier testing of multiple queries, we provide\n`scripts/generate_run_script.py` which creates a bash script with commands\nto run multiple query types in a row. The queries it generates should be\nput in a file with one query per line and the path given to the script.\nFor example, if you had a file named `queries.txt` that looked like this:\n```text\nlast-loc\navg-load\nhigh-load\nlong-driving-session\n```\n\nYou could generate a run script named `query_test.sh`:\n```bash\n# Generate run script for TimescaleDB, using queries in `queries.txt`\n# with the generated query files in /tmp/queries for 8 workers\n$ python generate_run_script.py -d timescaledb -o /tmp/queries \\\n    -w 8 -f queries.txt \u003e query_test.sh\n```\n\nAnd the resulting script file would look like:\n```bash\n#!/bin/bash\n# Queries\ncat /tmp/queries/timescaledb-last-loc-queries.gz | gunzip | query_benchmarker_timescaledb --workers=8 --limit=1000 --hosts=\"localhost\" --postgres=\"user=postgres sslmode=disable\"  | tee query_timescaledb_timescaledb-last-loc-queries.out\n\ncat /tmp/queries/timescaledb-avg-load-queries.gz | gunzip | query_benchmarker_timescaledb --workers=8 --limit=1000 --hosts=\"localhost\" --postgres=\"user=postgres sslmode=disable\"  | tee query_timescaledb_timescaledb-avg-load-queries.out\n\ncat /tmp/queries/timescaledb-high-load-queries.gz | gunzip | query_benchmarker_timescaledb --workers=8 --limit=1000 --hosts=\"localhost\" --postgres=\"user=postgres sslmode=disable\"  | tee query_timescaledb_timescaledb-high-load-queries.out\n\ncat /tmp/queries/timescaledb-long-driving-session-queries.gz | gunzip | query_benchmarker_timescaledb --workers=8 --limit=1000 --hosts=\"localhost\" --postgres=\"user=postgres sslmode=disable\"  | tee query_timescaledb_timescaledb-long-driving-session-queries.out\n```\n\n### Query validation (optional)\n\nAdditionally each `tsbs_run_queries_` binary allows you print the\nactual query results so that you can compare across databases that the\nresults are the same. Using the flag `-print-responses` will return\nthe results.\n\n## Appendix I: Query types \u003ca name=\"appendix-i-query-types\"\u003e\u003c/a\u003e\n\n### Devops / cpu-only\n|Query type|Description|\n|:---|:---|\n|single-groupby-1-1-1| Simple aggregrate (MAX) on one metric for 1 host, every 5 mins for 1 hour\n|single-groupby-1-1-12| Simple aggregrate (MAX) on one metric for 1 host, every 5 mins for 12 hours\n|single-groupby-1-8-1| Simple aggregrate (MAX) on one metric for 8 hosts, every 5 mins for 1 hour\n|single-groupby-5-1-1| Simple aggregrate (MAX) on 5 metrics for 1 host, every 5 mins for 1 hour\n|single-groupby-5-1-12| Simple aggregrate (MAX) on 5 metrics for 1 host, every 5 mins for 12 hours\n|single-groupby-5-8-1| Simple aggregrate (MAX) on 5 metrics for 8 hosts, every 5 mins for 1 hour\n|cpu-max-all-1| Aggregate across all CPU metrics per hour over 1 hour for a single host\n|cpu-max-all-8| Aggregate across all CPU metrics per hour over 1 hour for eight hosts\n|double-groupby-1| Aggregate on across both time and host, giving the average of 1 CPU metric per host per hour for 24 hours\n|double-groupby-5| Aggregate on across both time and host, giving the average of 5 CPU metrics per host per hour for 24 hours\n|double-groupby-all| Aggregate on across both time and host, giving the average of all (10) CPU metrics per host per hour for 24 hours\n|high-cpu-all| All the readings where one metric is above a threshold across all hosts\n|high-cpu-1| All the readings where one metric is above a threshold for a particular host\n|lastpoint| The last reading for each host\n|groupby-orderby-limit| The last 5 aggregate readings (across time) before a randomly chosen endpoint\n\n### IoT\n|Query type|Description|\n|:---|:---|\n|last-loc|Fetch real-time (i.e. last) location of each truck\n|low-fuel|Fetch all trucks with low fuel (less than 10%)\n|high-load|Fetch trucks with high current load (over 90% load capacity)\n|stationary-trucks|Fetch all trucks that are stationary (low avg velocity in last 10 mins)\n|long-driving-sessions|Get trucks which haven't rested for at least 20 mins in the last 4 hours\n|long-daily-sessions|Get trucks which drove more than 10 hours in the last 24 hours\n|avg-vs-projected-fuel-consumption|Calculate average vs. projected fuel consumption per fleet\n|avg-daily-driving-duration|Calculate average daily driving duration per driver\n|avg-daily-driving-session|Calculate average daily driving session per driver\n|avg-load|Calculate average load per truck model per fleet\n|daily-activity|Get the number of hours truck has been active (vs. out-of-commission) per day per fleet\n|breakdown-frequency|Calculate breakdown frequency by truck model\n\n## Contributing\n\nWe welcome contributions from the community to make TSBS better!\n\nYou can help either by opening an\n[issue](https://github.com/timescale/tsbs/issues) with\nany suggestions or bug reports, or by forking this repository,\nmaking your own contribution, and submitting a pull request.\n\nBefore we accept any contributions, Timescale contributors need to\nsign the [Contributor License Agreement](https://cla-assistant.io/timescale/tsbs) (CLA).\nBy signing a CLA, we can ensure that the community is free and confident in its\nability to use your contributions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimescale%2Ftsbs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimescale%2Ftsbs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimescale%2Ftsbs/lists"}