diff --git a/CLAUDE.md b/CLAUDE.md index 0696add95..9ddc3d580 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,6 +21,10 @@ Version control uses Jujutsu (`jj`) with Git. - Do **NOT** add `Co-Authored-By` lines to commit messages - Do **NOT** add `🤖 Generated with [Claude Code](https://claude.com/claude-code)` to PR descriptions +## Public Disclosure + +This is a public repository. **NEVER** disclose sensitive information about the production setup, data, or operations in any public-facing artifact: PR descriptions, PR/issue comments, commit messages, code comments, or docs. This includes production database sizes, row counts, table statistics, customer or project identifiers, incident details and timelines, and measured production performance characteristics. Describe changes in relative terms (e.g., "a large report" instead of actual production numbers) and keep concrete production figures in private channels. + ## Writing Style - **NEVER** use emdashes in any writing output: code comments, commit messages, PR descriptions, docs, issue/PR replies, and chat responses. Use a colon, semicolon, comma, parentheses, or two sentences instead. diff --git a/lib/api_projects/src/lib.rs b/lib/api_projects/src/lib.rs index 6bc09f197..fd20d04a0 100644 --- a/lib/api_projects/src/lib.rs +++ b/lib/api_projects/src/lib.rs @@ -23,6 +23,8 @@ mod thresholds; mod macros; +pub use reports::DELETE_CHUNK_SIZE; + pub struct Api; impl bencher_endpoint::Registrar for Api { diff --git a/lib/api_projects/src/reports.rs b/lib/api_projects/src/reports.rs index 0f4f1deb4..5063ecf8d 100644 --- a/lib/api_projects/src/reports.rs +++ b/lib/api_projects/src/reports.rs @@ -29,7 +29,7 @@ use bencher_schema::{ head::HeadId, version::{QueryVersion, VersionId}, }, - report::{NewRunReport, QueryReport, ReportId}, + report::{NewRunReport, QueryReport, ReportId, report_benchmark::ReportBenchmarkId}, }, user::{ actor::{ApiActor, PubProjectBearerToken}, @@ -446,6 +446,12 @@ pub async fn proj_report_delete( Ok(Delete::auth_response_deleted()) } +/// Number of `report_benchmark` rows deleted per write statement when +/// deleting a report's results. Bounds how long each delete holds the single +/// writer connection (validated at ~1s per chunk against a production-scale +/// report). +pub const DELETE_CHUNK_SIZE: i64 = 1024; + async fn delete_inner( context: &ApiContext, path_params: ProjReportParams, @@ -470,10 +476,15 @@ async fn delete_inner( Report, (&query_project, path_params.report) ))?; + delete_report_results(context, &query_project, report_id).await?; + diesel::delete(schema::report::table.filter(schema::report::id.eq(report_id))) .execute(write_conn!(context)) .map_err(resource_conflict_err!(Report, report_id))?; + #[cfg(feature = "otel")] + bencher_otel::ApiMeter::increment(bencher_otel::ApiCounter::ReportDelete); + // If there are no more reports for this version, delete the version // This is necessary because multiple reports can use the same version via a git hash // This will cascade and delete all head versions for this version @@ -549,8 +560,41 @@ async fn delete_inner( (&query_project, report_id, &query_version) ))?; - #[cfg(feature = "otel")] - bencher_otel::ApiMeter::increment(bencher_otel::ApiCounter::ReportDelete); - Ok(()) } + +/// Delete a report's results in bounded chunks, each in its own write +/// statement, so a large report does not hold the single writer connection +/// for its entire cascade (`report_benchmark` -> metric -> boundary -> alert). +/// Other writers interleave between chunks. Any rows that land between the +/// final chunk and the report row delete are cleaned up by the report's own +/// cascade. +/// +/// The deletion is deliberately not atomic: if the server fails mid-loop, the +/// report remains visible with a partial set of results (and a stale +/// `metric_count_by_report` rollup) until the delete is retried. This is +/// acceptable because reports are immutable after creation, the delete is +/// idempotent, and the report was already condemned by the caller. +async fn delete_report_results( + context: &ApiContext, + query_project: &QueryProject, + report_id: ReportId, +) -> Result<(), HttpError> { + loop { + let report_benchmark_ids = schema::report_benchmark::table + .filter(schema::report_benchmark::report_id.eq(report_id)) + .select(schema::report_benchmark::id) + .limit(DELETE_CHUNK_SIZE) + .load::(auth_conn!(context)) + .map_err(resource_not_found_err!(Report, (query_project, report_id)))?; + if report_benchmark_ids.is_empty() { + return Ok(()); + } + diesel::delete( + schema::report_benchmark::table + .filter(schema::report_benchmark::id.eq_any(report_benchmark_ids)), + ) + .execute(write_conn!(context)) + .map_err(resource_conflict_err!(Report, report_id))?; + } +} diff --git a/lib/api_projects/tests/reports.rs b/lib/api_projects/tests/reports.rs index 31a7f446a..f8d3379cd 100644 --- a/lib/api_projects/tests/reports.rs +++ b/lib/api_projects/tests/reports.rs @@ -6,10 +6,299 @@ )] //! Integration tests for project report endpoints. -use bencher_api_tests::TestServer; -use bencher_json::JsonReports; +use bencher_api_tests::{ + TestServer, + helpers::{base_timestamp, create_test_report, get_project_id}, +}; +use bencher_json::{ + BenchmarkUuid, BoundaryUuid, JsonReports, MeasureUuid, MetricUuid, ModelUuid, + ReportBenchmarkUuid, ThresholdUuid, +}; +use bencher_schema::{ + context::DbConnection, + model::project::report::{ReportId, upsert_metric_count}, + schema, +}; +use diesel::{ExpressionMethods as _, QueryDsl as _, RunQueryDsl as _}; use http::StatusCode; +/// Insert the benchmark, measure, threshold, and model rows needed for +/// report results. Returns (`benchmark_id`, `measure_id`, `threshold_id`, `model_id`). +#[expect(clippy::expect_used, reason = "test helper")] +fn seed_result_infra( + conn: &mut DbConnection, + project_id: i32, + branch_id: i32, + testbed_id: i32, +) -> (i32, i32, i32, i32) { + let now = base_timestamp(); + + let benchmark_uuid = BenchmarkUuid::new(); + diesel::insert_into(schema::benchmark::table) + .values(( + schema::benchmark::uuid.eq(&benchmark_uuid), + schema::benchmark::project_id.eq(project_id), + schema::benchmark::name.eq("test-benchmark"), + schema::benchmark::slug.eq(&format!("test-benchmark-{benchmark_uuid}")), + schema::benchmark::created.eq(&now), + schema::benchmark::modified.eq(&now), + )) + .execute(&mut *conn) + .expect("Failed to insert benchmark"); + let benchmark_id: i32 = schema::benchmark::table + .filter(schema::benchmark::uuid.eq(&benchmark_uuid)) + .select(schema::benchmark::id) + .first(&mut *conn) + .expect("Failed to get benchmark ID"); + + let measure_uuid = MeasureUuid::new(); + diesel::insert_into(schema::measure::table) + .values(( + schema::measure::uuid.eq(&measure_uuid), + schema::measure::project_id.eq(project_id), + schema::measure::name.eq("test-measure"), + schema::measure::slug.eq(&format!("test-measure-{measure_uuid}")), + schema::measure::units.eq("ns"), + schema::measure::created.eq(&now), + schema::measure::modified.eq(&now), + )) + .execute(&mut *conn) + .expect("Failed to insert measure"); + let measure_id: i32 = schema::measure::table + .filter(schema::measure::uuid.eq(&measure_uuid)) + .select(schema::measure::id) + .first(&mut *conn) + .expect("Failed to get measure ID"); + + let threshold_uuid = ThresholdUuid::new(); + diesel::insert_into(schema::threshold::table) + .values(( + schema::threshold::uuid.eq(&threshold_uuid), + schema::threshold::project_id.eq(project_id), + schema::threshold::branch_id.eq(branch_id), + schema::threshold::testbed_id.eq(testbed_id), + schema::threshold::measure_id.eq(measure_id), + schema::threshold::created.eq(&now), + schema::threshold::modified.eq(&now), + )) + .execute(&mut *conn) + .expect("Failed to insert threshold"); + let threshold_id: i32 = schema::threshold::table + .filter(schema::threshold::uuid.eq(&threshold_uuid)) + .select(schema::threshold::id) + .first(&mut *conn) + .expect("Failed to get threshold ID"); + + let model_uuid = ModelUuid::new(); + diesel::insert_into(schema::model::table) + .values(( + schema::model::uuid.eq(&model_uuid), + schema::model::threshold_id.eq(threshold_id), + schema::model::test.eq(0), + schema::model::created.eq(&now), + )) + .execute(&mut *conn) + .expect("Failed to insert model"); + let model_id: i32 = schema::model::table + .filter(schema::model::uuid.eq(&model_uuid)) + .select(schema::model::id) + .first(conn) + .expect("Failed to get model ID"); + + (benchmark_id, measure_id, threshold_id, model_id) +} + +/// Seed `count` report results (`report_benchmark` -> metric -> boundary rows) +/// for a report created via `create_test_report`. Uses one benchmark with +/// `count` iterations. Returns the report UUID for the delete URL. +#[expect(clippy::expect_used, reason = "test helper")] +fn seed_report_results(server: &TestServer, project_id: i32, report_id: i32, count: i32) -> String { + // Batch size per INSERT statement, kept well under SQLite's bind limit. + const INSERT_BATCH: usize = 1024; + + let mut conn = server.db_conn(); + + let (report_uuid, testbed_id, head_id): (String, i32, i32) = schema::report::table + .filter(schema::report::id.eq(report_id)) + .select(( + schema::report::uuid, + schema::report::testbed_id, + schema::report::head_id, + )) + .first(&mut conn) + .expect("Failed to get report"); + let branch_id: i32 = schema::head::table + .filter(schema::head::id.eq(head_id)) + .select(schema::head::branch_id) + .first(&mut conn) + .expect("Failed to get branch ID"); + + let (benchmark_id, measure_id, threshold_id, model_id) = + seed_result_infra(&mut conn, project_id, branch_id, testbed_id); + + let report_benchmarks = (0..count) + .map(|iteration| { + ( + schema::report_benchmark::uuid.eq(ReportBenchmarkUuid::new()), + schema::report_benchmark::report_id.eq(report_id), + schema::report_benchmark::iteration.eq(iteration), + schema::report_benchmark::benchmark_id.eq(benchmark_id), + ) + }) + .collect::>(); + for batch in report_benchmarks.chunks(INSERT_BATCH) { + diesel::insert_into(schema::report_benchmark::table) + .values(batch.to_vec()) + .execute(&mut conn) + .expect("Failed to insert report benchmarks"); + } + let report_benchmark_ids: Vec = schema::report_benchmark::table + .filter(schema::report_benchmark::report_id.eq(report_id)) + .select(schema::report_benchmark::id) + .load(&mut conn) + .expect("Failed to get report benchmark IDs"); + + let metrics = report_benchmark_ids + .iter() + .map(|report_benchmark_id| { + ( + schema::metric::uuid.eq(MetricUuid::new()), + schema::metric::report_benchmark_id.eq(*report_benchmark_id), + schema::metric::measure_id.eq(measure_id), + schema::metric::value.eq(42.0), + ) + }) + .collect::>(); + for batch in metrics.chunks(INSERT_BATCH) { + diesel::insert_into(schema::metric::table) + .values(batch.to_vec()) + .execute(&mut conn) + .expect("Failed to insert metrics"); + } + let metric_ids: Vec = schema::metric::table + .filter(schema::metric::report_benchmark_id.eq_any(&report_benchmark_ids)) + .select(schema::metric::id) + .load(&mut conn) + .expect("Failed to get metric IDs"); + + let boundaries = metric_ids + .iter() + .map(|metric_id| { + ( + schema::boundary::uuid.eq(BoundaryUuid::new()), + schema::boundary::metric_id.eq(*metric_id), + schema::boundary::threshold_id.eq(threshold_id), + schema::boundary::model_id.eq(model_id), + schema::boundary::baseline.eq(42.0), + schema::boundary::upper_limit.eq(100.0), + ) + }) + .collect::>(); + for batch in boundaries.chunks(INSERT_BATCH) { + diesel::insert_into(schema::boundary::table) + .values(batch.to_vec()) + .execute(&mut conn) + .expect("Failed to insert boundaries"); + } + + let report_id = ReportId::try_from_raw(report_id).expect("valid report ID"); + upsert_metric_count(&mut conn, report_id, count).expect("Failed to upsert metric count"); + + report_uuid +} + +/// Count the report, `report_benchmark`, metric, and boundary rows for a report. +#[expect(clippy::expect_used, reason = "test helper")] +fn report_row_counts(server: &TestServer, report_id: i32) -> (i64, i64, i64, i64) { + let mut conn = server.db_conn(); + let reports: i64 = schema::report::table + .filter(schema::report::id.eq(report_id)) + .count() + .first(&mut conn) + .expect("Failed to count reports"); + let report_benchmark_ids: Vec = schema::report_benchmark::table + .filter(schema::report_benchmark::report_id.eq(report_id)) + .select(schema::report_benchmark::id) + .load(&mut conn) + .expect("Failed to get report benchmark IDs"); + let metric_ids: Vec = schema::metric::table + .filter(schema::metric::report_benchmark_id.eq_any(&report_benchmark_ids)) + .select(schema::metric::id) + .load(&mut conn) + .expect("Failed to get metric IDs"); + let boundaries: i64 = schema::boundary::table + .filter(schema::boundary::metric_id.eq_any(&metric_ids)) + .count() + .first(&mut conn) + .expect("Failed to count boundaries"); + #[expect(clippy::cast_possible_wrap, reason = "test row counts")] + ( + reports, + report_benchmark_ids.len() as i64, + metric_ids.len() as i64, + boundaries, + ) +} + +/// Delete a report and assert that all of its result rows are gone. +#[expect(clippy::expect_used, reason = "test helper")] +async fn delete_report_and_assert_empty(count: i32) { + let server = TestServer::new().await; + let user = server + .signup("Test User", "reportdelresults@example.com") + .await; + let org = server.create_org(&user, "Report Del Results Org").await; + let project = server + .create_project(&user, &org, "Report Del Results Project") + .await; + + let project_slug: &str = project.slug.as_ref(); + let project_id = get_project_id(&server, project_slug); + let report_id = create_test_report(&server, project_id); + let report_uuid = seed_report_results(&server, project_id, report_id, count); + + let counts = report_row_counts(&server, report_id); + assert_eq!( + counts, + (1, i64::from(count), i64::from(count), i64::from(count)), + "seeded report rows" + ); + + let resp = server + .client + .delete(server.api_url(&format!( + "/v0/projects/{}/reports/{}", + project_slug, report_uuid + ))) + .header( + bencher_json::AUTHORIZATION, + bencher_json::bearer_header(&user.token), + ) + .send() + .await + .expect("Request failed"); + assert_eq!(resp.status(), StatusCode::NO_CONTENT, "delete report"); + + let counts = report_row_counts(&server, report_id); + assert_eq!(counts, (0, 0, 0, 0), "report rows after delete"); +} + +// DELETE /v0/projects/{project}/reports/{report} - report with results +// (fewer rows than one delete chunk) +#[tokio::test] +async fn reports_delete_with_results() { + delete_report_and_assert_empty(5).await; +} + +// DELETE /v0/projects/{project}/reports/{report} - report with exactly +// 2 * DELETE_CHUNK_SIZE results, exercising multiple delete chunks and the +// exact-multiple boundary condition +#[tokio::test] +async fn reports_delete_chunked() { + let count = i32::try_from(2 * api_projects::DELETE_CHUNK_SIZE).expect("count fits in i32"); + delete_report_and_assert_empty(count).await; +} + // GET /v0/projects/{project}/reports - list reports (empty) #[tokio::test] async fn reports_list_empty() { diff --git a/lib/api_run/Cargo.toml b/lib/api_run/Cargo.toml index 966a690fe..cb6b2fe70 100644 --- a/lib/api_run/Cargo.toml +++ b/lib/api_run/Cargo.toml @@ -29,7 +29,7 @@ diesel.workspace = true http.workspace = true serde_json.workspace = true tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } -uuid.workspace = true +uuid = { workspace = true, features = ["v4"] } [lints] workspace = true diff --git a/lib/bencher_config/src/config_tx.rs b/lib/bencher_config/src/config_tx.rs index 73ec6fe82..024301e75 100644 --- a/lib/bencher_config/src/config_tx.rs +++ b/lib/bencher_config/src/config_tx.rs @@ -44,7 +44,7 @@ use slog::{Logger, debug, error, info}; #[cfg(feature = "plus")] use super::plus::Plus; -use super::{Config, DEFAULT_BUSY_TIMEOUT}; +use super::{Config, DEFAULT_BUSY_TIMEOUT, DEFAULT_CACHE_SIZE}; const DATABASE_URL: &str = "DATABASE_URL"; const SQLITE_TMPDIR: &str = "SQLITE_TMPDIR"; @@ -224,9 +224,10 @@ async fn into_context( // busy_timeout prevents immediate SQLITE_BUSY errors under lock contention. // synchronous=NORMAL is safe with WAL mode and reduces fsync overhead. let busy_timeout = json_database.busy_timeout.unwrap_or(DEFAULT_BUSY_TIMEOUT); + let cache_size = json_database.cache_size.unwrap_or(DEFAULT_CACHE_SIZE); info!( log, - "Setting database PRAGMAs (busy_timeout: {busy_timeout}ms)" + "Setting database PRAGMAs (busy_timeout: {busy_timeout}ms, cache_size: {cache_size} KiB)" ); database_connection .batch_execute("PRAGMA journal_mode = WAL") @@ -237,6 +238,12 @@ async fn into_context( database_connection .batch_execute("PRAGMA synchronous = NORMAL") .map_err(ConfigTxError::Pragma)?; + // Writer connection only: the read pools hold many connections, so a + // large per-connection cache would multiply memory, and reads are served + // by the OS page cache. Negative value = KiB in SQLite. + database_connection + .batch_execute(&format!("PRAGMA cache_size = -{cache_size}")) + .map_err(ConfigTxError::Pragma)?; // Surfaces `SQLITE_BUSY_SNAPSHOT` (517) etc. distinctly from plain `SQLITE_BUSY` (5) // in error messages, instead of both rendering as "database is locked". database_connection diff --git a/lib/bencher_config/src/lib.rs b/lib/bencher_config/src/lib.rs index 940056e62..e69f37881 100644 --- a/lib/bencher_config/src/lib.rs +++ b/lib/bencher_config/src/lib.rs @@ -1,6 +1,7 @@ use std::sync::LazyLock; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, + num::NonZeroU32, ops::Deref, }; @@ -47,6 +48,14 @@ const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; const DEFAULT_BUSY_TIMEOUT: u32 = 5_000; +// 64 MiB, in KiB. Large report deletions and ingests dirty far more pages +// than the SQLite default (2 MiB); a bigger writer cache avoids re-reading +// evicted pages mid-operation. +const DEFAULT_CACHE_SIZE: NonZeroU32 = match NonZeroU32::new(0x1_0000) { + Some(cache_size) => cache_size, + None => panic!("default cache size is zero"), +}; + const DEFAULT_CONSOLE_URL_STR: &str = "http://localhost:3000"; #[expect(clippy::panic, reason = "compile-time constant URL must be valid")] static DEFAULT_CONSOLE_URL: LazyLock = LazyLock::new(|| { @@ -220,6 +229,7 @@ impl Default for Config { file: DEFAULT_DB_PATH.into(), data_store: None, busy_timeout: None, + cache_size: None, }, smtp: None, logging: JsonLogging { diff --git a/lib/bencher_json/Cargo.toml b/lib/bencher_json/Cargo.toml index 1c34124b3..ee0632ea9 100644 --- a/lib/bencher_json/Cargo.toml +++ b/lib/bencher_json/Cargo.toml @@ -33,7 +33,7 @@ tabled = { workspace = true, optional = true } thiserror.workspace = true typeshare.workspace = true url = { workspace = true, features = ["serde"] } -uuid = { workspace = true, features = ["v4", "serde"] } +uuid = { workspace = true, features = ["v7", "serde"] } [dev-dependencies] pretty_assertions.workspace = true diff --git a/lib/bencher_json/src/system/config/database.rs b/lib/bencher_json/src/system/config/database.rs index 1fff4b4fb..bd76ca664 100644 --- a/lib/bencher_json/src/system/config/database.rs +++ b/lib/bencher_json/src/system/config/database.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{num::NonZeroU32, path::PathBuf}; use bencher_valid::{Sanitize, Secret}; #[cfg(feature = "schema")] @@ -14,6 +14,9 @@ pub struct JsonDatabase { /// The database busy timeout in milliseconds #[serde(skip_serializing_if = "Option::is_none")] pub busy_timeout: Option, + /// The database page cache size in KiB for the writer connection + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_size: Option, } impl Sanitize for JsonDatabase { diff --git a/lib/bencher_json/src/typed_uuid.rs b/lib/bencher_json/src/typed_uuid.rs index a621db9ca..b63fb5cbc 100644 --- a/lib/bencher_json/src/typed_uuid.rs +++ b/lib/bencher_json/src/typed_uuid.rs @@ -61,8 +61,11 @@ macro_rules! typed_uuid { } impl $uuid { + // Time-ordered UUIDv7 keeps same-batch rows adjacent in the + // database's uuid indexes, avoiding the B-tree page scatter that + // random v4 uuids cause on bulk insert and delete. pub fn new() -> Self { - Self(uuid::Uuid::new_v4()) + Self(uuid::Uuid::now_v7()) } } @@ -71,3 +74,14 @@ macro_rules! typed_uuid { } pub(crate) use typed_uuid; + +#[cfg(test)] +mod tests { + use crate::project::metric::MetricUuid; + + #[test] + fn typed_uuid_new_is_v7() { + let uuid: uuid::Uuid = MetricUuid::new().into(); + assert_eq!(uuid.get_version_num(), 7); + } +} diff --git a/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/down.sql b/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/down.sql new file mode 100644 index 000000000..6d0f0c750 --- /dev/null +++ b/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/down.sql @@ -0,0 +1,2 @@ +CREATE INDEX index_report_benchmark_benchmark ON report_benchmark(benchmark_id); +CREATE INDEX index_report_benchmark ON report_benchmark(report_id, benchmark_id); diff --git a/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/up.sql b/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/up.sql new file mode 100644 index 000000000..62a627d1e --- /dev/null +++ b/lib/bencher_schema/migrations/2026-07-07-120000_report_benchmark_index_cleanup/up.sql @@ -0,0 +1,4 @@ +-- Redundant with index_report_benchmark_benchmark_report(benchmark_id, report_id) +DROP INDEX IF EXISTS index_report_benchmark_benchmark; +-- Redundant with the UNIQUE(report_id, iteration, benchmark_id) autoindex +DROP INDEX IF EXISTS index_report_benchmark; diff --git a/services/api/openapi.json b/services/api/openapi.json index eeca82ae3..cd08d0954 100644 --- a/services/api/openapi.json +++ b/services/api/openapi.json @@ -12657,6 +12657,13 @@ "format": "uint32", "minimum": 0 }, + "cache_size": { + "nullable": true, + "description": "The database page cache size in KiB for the writer connection", + "type": "integer", + "format": "uint32", + "minimum": 1 + }, "data_store": { "nullable": true, "allOf": [ diff --git a/services/cli/Cargo.toml b/services/cli/Cargo.toml index 1556572d9..a6324cccf 100644 --- a/services/cli/Cargo.toml +++ b/services/cli/Cargo.toml @@ -38,7 +38,7 @@ thiserror.workspace = true tokio = { workspace = true, features = ["macros", "process", "rt", "signal"] } tokio-rustls.workspace = true url.workspace = true -uuid.workspace = true +uuid = { workspace = true, features = ["v4"] } [lints] workspace = true diff --git a/services/console/src/chunks/docs-reference/changelog/en/changelog.mdx b/services/console/src/chunks/docs-reference/changelog/en/changelog.mdx index 8d68d2146..9614fa332 100644 --- a/services/console/src/chunks/docs-reference/changelog/en/changelog.mdx +++ b/services/console/src/chunks/docs-reference/changelog/en/changelog.mdx @@ -1,3 +1,8 @@ +## Pending `v0.6.9` +- Speed up large report deletions: delete report results in bounded chunks so a large report delete no longer blocks all other database writes for its full duration +- Mint time-ordered UUIDv7 instead of random UUIDv4 for new resources, clustering database index writes for bulk inserts and deletes +- Add a `database.cache_size` server config option for the writer connection page cache in KiB (default 64 MiB) + ## `v0.6.8` - **BREAKING CHANGE** Change the default self-hosted API server port from `61016` to the newly [IANA-registered](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=6610) `6610`. Deployments relying on the default now serve the API on `6610`. Update clients, reverse proxies, and `--host`/`BENCHER_HOST` references (Docker Compose, devcontainer, and docs are updated to match). To stay on `61016`, set `server.bind_address` to `0.0.0.0:61016` (or run `bencher up --api-port 61016`). - Mark the remaining user API token REST endpoints (list, view, update, and revoke) as deprecated in the OpenAPI spec and API docs; they continue to work for existing tokens diff --git a/services/console/src/chunks/docs-reference/server-config/de/database.mdx b/services/console/src/chunks/docs-reference/server-config/de/database.mdx index 5d5a3a964..8c8c27849 100644 --- a/services/console/src/chunks/docs-reference/server-config/de/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/de/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Nur wenn data_store.service = "aws_s3" | Wenn data_store.service "aws_s3" ist, gibt diese Eigenschaft den AWS geheimen Zugangsschlüssel an. Siehe auch data_store.service. Im Protokoll erscheint es als `************`. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/pfad/zu/backup/dir" | --- | Nur wenn data_store.service = "aws_s3" | Wenn data_store.service "aws_s3" ist, gibt diese Eigenschaft den [Zugangspunkt von AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html) an. Siehe auch data_store.service. | | busy_timeout | 5000 | 5000 | Nein | Gibt den Busy-Timeout für die Datenbank in Millisekunden an. Verhindert sofortige SQLITE_BUSY-Fehler bei Sperrkonflikten. | +| cache_size | 65536 | 65536 | Nein | Gibt die Seiten-Cache-Größe in KiB für die Schreibverbindung der Datenbank an. Ein größerer Cache vermeidet das erneute Lesen verdrängter Seiten bei großen Report-Importen und -Löschungen. Muss größer als 0 sein. | diff --git a/services/console/src/chunks/docs-reference/server-config/en/database.mdx b/services/console/src/chunks/docs-reference/server-config/en/database.mdx index 0deb6c265..c4f43fd71 100644 --- a/services/console/src/chunks/docs-reference/server-config/en/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/en/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Only if data_store.service = "aws_s3" | If data_store.service = "aws_s3", this property specifies the AWS secret access key. See also data_store.service. Whenever logged, it will appear obfuscated as `************`. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | Only if data_store.service = "aws_s3" | If data_store.service = "aws_s3", this property specifies the [AWS S3 accesspoint](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html). See also data_store.service. | | busy_timeout | 5000 | 5000 | No | Specifies the busy timeout for the database in milliseconds. Prevents immediate SQLITE_BUSY errors under lock contention. | +| cache_size | 65536 | 65536 | No | Specifies the page cache size in KiB for the writer database connection. A larger cache avoids re-reading evicted pages during large report ingests and deletions. Must be greater than 0. | diff --git a/services/console/src/chunks/docs-reference/server-config/es/database.mdx b/services/console/src/chunks/docs-reference/server-config/es/database.mdx index 549ce327b..c61ded1c8 100644 --- a/services/console/src/chunks/docs-reference/server-config/es/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/es/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Solo si data_store.service = "aws_s3" | Si data_store.service = "aws_s3", esta propiedad especifica la clave de acceso secreta de AWS. Ver también data_store.service. Cuando se registra, aparecerá ofuscado como `************`. | | data_store.access_point | "arn:aws:s3:alguna-region-1:123456789:accesspoint/mi-bucket/ruta/hacia/directorio/de/respaldo" | --- | Solo si data_store.service = "aws_s3" | Si data_store.service = "aws_s3", esta propiedad especifica el [punto de acceso S3 de AWS](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html). Ver también data_store.service. | | busy_timeout | 5000 | 5000 | No | Especifica el tiempo de espera ocupado para la base de datos en milisegundos. Previene errores SQLITE_BUSY inmediatos bajo contención de bloqueo. | +| cache_size | 65536 | 65536 | No | Especifica el tamaño de la caché de páginas en KiB para la conexión de escritura de la base de datos. Una caché más grande evita releer páginas expulsadas durante ingestas y eliminaciones de informes grandes. Debe ser mayor que 0. | diff --git a/services/console/src/chunks/docs-reference/server-config/example.mdx b/services/console/src/chunks/docs-reference/server-config/example.mdx index dba513514..20b047335 100644 --- a/services/console/src/chunks/docs-reference/server-config/example.mdx +++ b/services/console/src/chunks/docs-reference/server-config/example.mdx @@ -32,7 +32,8 @@ "secret_access_key": "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7", "access_point": "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" }, - "busy_timeout": 5000 + "busy_timeout": 5000, + "cache_size": 65536 }, "smtp": { "hostname": "mailbonobo.com", diff --git a/services/console/src/chunks/docs-reference/server-config/fr/database.mdx b/services/console/src/chunks/docs-reference/server-config/fr/database.mdx index a328c62a2..87858955d 100644 --- a/services/console/src/chunks/docs-reference/server-config/fr/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/fr/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Seulement si data_store.service = "aws_s3" | Si data_store.service = "aws_s3", cette propriété spécifie la clé d'accès secrète AWS. Voir aussi data_store.service. Lorsqu'elle est enregistrée, elle apparaîtra sous forme obfusquée, c'est-à-dire `************`. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | Seulement si data_store.service = "aws_s3" | Si data_store.service = "aws_s3", cette propriété spécifie le [point d'accès AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html). Voir aussi data_store.service. | | busy_timeout | 5000 | 5000 | Non | Spécifie le délai d'attente pour la base de données en millisecondes. Empêche les erreurs SQLITE_BUSY immédiates en cas de contention de verrou. | +| cache_size | 65536 | 65536 | Non | Spécifie la taille du cache de pages en KiB pour la connexion d'écriture de la base de données. Un cache plus grand évite de relire les pages évincées lors des ingestions et suppressions de rapports volumineux. Doit être supérieur à 0. | diff --git a/services/console/src/chunks/docs-reference/server-config/ja/database.mdx b/services/console/src/chunks/docs-reference/server-config/ja/database.mdx index 0a3225592..37cd8a976 100644 --- a/services/console/src/chunks/docs-reference/server-config/ja/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/ja/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | data_store.service = "aws_s3"の場合のみ | data_store.service = "aws_s3" の場合、このプロパティは AWS シークレットアクセスキーを指定します。 data_store.service も参照してください。ログ時には `************` として表示されます。 | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | data_store.service = "aws_s3"の場合のみ | data_store.service = "aws_s3" の場合、このプロパティは [AWS S3 アクセスポイント](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html) を指定します。 data_store.service も参照してください。 | | busy_timeout | 5000 | 5000 | いいえ | データベースのビジータイムアウトをミリ秒単位で指定します。ロック競合下での即座のSQLITE_BUSYエラーを防止します。 | +| cache_size | 65536 | 65536 | いいえ | 書き込み用データベース接続のページキャッシュサイズをKiB単位で指定します。キャッシュを大きくすると、大規模なレポートの取り込みや削除の際に追い出されたページの再読み込みを回避できます。 0より大きい値でなければなりません。 | diff --git a/services/console/src/chunks/docs-reference/server-config/ko/database.mdx b/services/console/src/chunks/docs-reference/server-config/ko/database.mdx index ded3c9d59..937933a8c 100644 --- a/services/console/src/chunks/docs-reference/server-config/ko/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/ko/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | data_store.service = "aws_s3"인 경우만 | data_store.service = "aws_s3"인 경우, 이 속성은 AWS secret access key를 지정합니다. data_store.service에 대해서도 참조하십시오. 로그에 기록될 때 `************`로 표현됩니다. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | data_store.service = "aws_s3"인 경우만 | data_store.service = "aws_s3"인 경우, 이 속성은 [AWS S3 accesspoint](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html)를 지정합니다. data_store.service에 대해서도 참조하십시오. | | busy_timeout | 5000 | 5000 | 아니오 | 데이터베이스의 busy 타임아웃을 밀리초(ms) 단위로 지정합니다. 잠금 경합 시 즉각적인 SQLITE_BUSY 오류를 방지합니다. | +| cache_size | 65536 | 65536 | 아니오 | 쓰기 데이터베이스 연결의 페이지 캐시 크기를 KiB 단위로 지정합니다. 캐시가 클수록 대규모 리포트 수집 및 삭제 시 축출된 페이지를 다시 읽는 것을 방지합니다. 0보다 커야 합니다. | diff --git a/services/console/src/chunks/docs-reference/server-config/pt/database.mdx b/services/console/src/chunks/docs-reference/server-config/pt/database.mdx index 36305ec25..130e1403d 100644 --- a/services/console/src/chunks/docs-reference/server-config/pt/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/pt/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Apenas se data_store.service = "aws_s3" | Se data_store.service = "aws_s3", esta propriedade especifica a chave de acesso secreta do AWS. Veja também data_store.service. Quando registrada, ela aparecerá obfuscada como `************`. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | Apenas se data_store.service = "aws_s3" | Se data_store.service = "aws_s3", esta propriedade especifica o [ponto de acesso do AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html). Veja também data_store.service. | | busy_timeout | 5000 | 5000 | Não | Especifica o tempo limite de ocupação para o banco de dados em milissegundos. Previne erros SQLITE_BUSY imediatos sob contenção de bloqueio. | +| cache_size | 65536 | 65536 | Não | Especifica o tamanho do cache de páginas em KiB para a conexão de escrita do banco de dados. Um cache maior evita a releitura de páginas removidas durante ingestões e exclusões de relatórios grandes. Deve ser maior que 0. | diff --git a/services/console/src/chunks/docs-reference/server-config/ru/database.mdx b/services/console/src/chunks/docs-reference/server-config/ru/database.mdx index 7a38754a5..15b3983d1 100644 --- a/services/console/src/chunks/docs-reference/server-config/ru/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/ru/database.mdx @@ -8,4 +8,5 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | Только если data_store.service = "aws_s3" | Если data_store.service = "aws_s3", это свойство указывает секретный ключ доступа AWS. Смотрите также data_store.service. При логировании он будет обфусцирован как `************`. | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | Только если data_store.service = "aws_s3" | Если data_store.service = "aws_s3", это свойство указывает [точку доступа AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html). Смотрите также data_store.service. | | busy_timeout | 5000 | 5000 | Нет | Указывает тайм-аут занятости для базы данных в миллисекундах. Предотвращает немедленные ошибки SQLITE_BUSY при блокировке. | +| cache_size | 65536 | 65536 | Нет | Указывает размер страничного кэша в КиБ для соединения записи базы данных. Больший кэш позволяет избежать повторного чтения вытесненных страниц при больших загрузках и удалениях отчетов. Должен быть больше 0. | diff --git a/services/console/src/chunks/docs-reference/server-config/zh/database.mdx b/services/console/src/chunks/docs-reference/server-config/zh/database.mdx index 3138ffe40..7c48116de 100644 --- a/services/console/src/chunks/docs-reference/server-config/zh/database.mdx +++ b/services/console/src/chunks/docs-reference/server-config/zh/database.mdx @@ -8,3 +8,4 @@ | data_store.secret_access_key | "AA3Chr-JSF5sUQqKwayx-FvCfZKsMev-5BqPpcFC3m7" | --- | 仅在 data_store.service = "aws_s3"时 | 如果 data_store.service = "aws_s3",此属性指定 AWS 私密访问密钥。参见 data_store.service。当进行记录时,它将显示为 `************`。 | | data_store.access_point | "arn:aws:s3:some-region-1:123456789:accesspoint/my-bucket/path/to/backup/dir" | --- | 仅在 data_store.service = "aws_s3"时 | 如果 data_store.service 是 "aws_s3",此属性指定 [AWS S3 接入点](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html)。参见 data_store.service。 | | busy_timeout | 5000 | 5000 | 否 | 指定数据库的忙碌超时时间,以毫秒为单位。防止在锁争用时立即出现SQLITE_BUSY错误。 | +| cache_size | 65536 | 65536 | 否 | 指定写入数据库连接的页面缓存大小,以KiB为单位。更大的缓存可以避免在大型报告摄取和删除期间重新读取被逐出的页面。 必须大于0。 | diff --git a/services/console/src/content/docs-reference/de/changelog.mdx b/services/console/src/content/docs-reference/de/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/de/changelog.mdx +++ b/services/console/src/content/docs-reference/de/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/de/server-config.mdx b/services/console/src/content/docs-reference/de/server-config.mdx index 020d75ace..ba0bb3da8 100644 --- a/services/console/src/content/docs-reference/de/server-config.mdx +++ b/services/console/src/content/docs-reference/de/server-config.mdx @@ -3,7 +3,7 @@ title: "Server-Konfiguration" description: "Bencher API-Server-Konfiguration für kontinuierliches Benchmarking im Eigenbetrieb" heading: "API-Server-Konfiguration" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/en/changelog.mdx b/services/console/src/content/docs-reference/en/changelog.mdx index 784f83568..14394c2d5 100644 --- a/services/console/src/content/docs-reference/en/changelog.mdx +++ b/services/console/src/content/docs-reference/en/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher release changelog" heading: "Bencher Changelog" published: "2023-08-12T16:07:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 --- diff --git a/services/console/src/content/docs-reference/en/server-config.mdx b/services/console/src/content/docs-reference/en/server-config.mdx index dfa10d100..e047a9130 100644 --- a/services/console/src/content/docs-reference/en/server-config.mdx +++ b/services/console/src/content/docs-reference/en/server-config.mdx @@ -3,7 +3,7 @@ title: "API Server Config" description: "Bencher API server configuration for self-hosted continuous benchmarking" heading: "API Server Config" published: "2023-08-12T16:07:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/es/changelog.mdx b/services/console/src/content/docs-reference/es/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/es/changelog.mdx +++ b/services/console/src/content/docs-reference/es/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/es/server-config.mdx b/services/console/src/content/docs-reference/es/server-config.mdx index 730131964..ecad8aa46 100644 --- a/services/console/src/content/docs-reference/es/server-config.mdx +++ b/services/console/src/content/docs-reference/es/server-config.mdx @@ -3,7 +3,7 @@ title: "Configuración del Servidor" description: "Configuración del servidor API de Bencher para benchmarking continuo autoalojado" heading: "Configuración del Servidor API" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/fr/changelog.mdx b/services/console/src/content/docs-reference/fr/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/fr/changelog.mdx +++ b/services/console/src/content/docs-reference/fr/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/fr/server-config.mdx b/services/console/src/content/docs-reference/fr/server-config.mdx index 0fdc61168..396f47826 100644 --- a/services/console/src/content/docs-reference/fr/server-config.mdx +++ b/services/console/src/content/docs-reference/fr/server-config.mdx @@ -3,7 +3,7 @@ title: "Config Serveur" description: "Configuration du serveur API Bencher pour le benchmarking continu auto-hébergé" heading: "Config Serveur API" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/ja/changelog.mdx b/services/console/src/content/docs-reference/ja/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/ja/changelog.mdx +++ b/services/console/src/content/docs-reference/ja/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/ja/server-config.mdx b/services/console/src/content/docs-reference/ja/server-config.mdx index 445478081..369c25f19 100644 --- a/services/console/src/content/docs-reference/ja/server-config.mdx +++ b/services/console/src/content/docs-reference/ja/server-config.mdx @@ -3,7 +3,7 @@ title: "サーバー設定" description: "自己ホスト型の継続的なベンチマーキングのためのBencher APIサーバー設定" heading: "APIサーバー設定" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/ko/changelog.mdx b/services/console/src/content/docs-reference/ko/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/ko/changelog.mdx +++ b/services/console/src/content/docs-reference/ko/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/ko/server-config.mdx b/services/console/src/content/docs-reference/ko/server-config.mdx index 17f7d2b34..05cbca83d 100644 --- a/services/console/src/content/docs-reference/ko/server-config.mdx +++ b/services/console/src/content/docs-reference/ko/server-config.mdx @@ -3,7 +3,7 @@ title: "서버 설정" description: "자체 호스팅을 위한 지속적인 벤치마킹을 위한 Bencher API 서버 구성" heading: "API 서버 설정" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/pt/changelog.mdx b/services/console/src/content/docs-reference/pt/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/pt/changelog.mdx +++ b/services/console/src/content/docs-reference/pt/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/pt/server-config.mdx b/services/console/src/content/docs-reference/pt/server-config.mdx index 82d3f277b..bfa79afd9 100644 --- a/services/console/src/content/docs-reference/pt/server-config.mdx +++ b/services/console/src/content/docs-reference/pt/server-config.mdx @@ -3,7 +3,7 @@ title: "Configuração do Servidor" description: "Configuração do servidor da API Bencher para benchmarking contínuo auto-hospedado" heading: "Configuração do Servidor API" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/ru/changelog.mdx b/services/console/src/content/docs-reference/ru/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/ru/changelog.mdx +++ b/services/console/src/content/docs-reference/ru/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/ru/server-config.mdx b/services/console/src/content/docs-reference/ru/server-config.mdx index 0467c84d8..d3a0ea465 100644 --- a/services/console/src/content/docs-reference/ru/server-config.mdx +++ b/services/console/src/content/docs-reference/ru/server-config.mdx @@ -3,7 +3,7 @@ title: "Конфигурация сервера" description: "Конфигурация сервера API Bencher для самостоятельного непрерывного бенчмаркинга" heading: "Конфигурация API сервера" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 --- diff --git a/services/console/src/content/docs-reference/zh/changelog.mdx b/services/console/src/content/docs-reference/zh/changelog.mdx index 06c043d91..42422c875 100644 --- a/services/console/src/content/docs-reference/zh/changelog.mdx +++ b/services/console/src/content/docs-reference/zh/changelog.mdx @@ -3,7 +3,7 @@ title: "Changelog" description: "Bencher changelog" heading: "Bencher Changelog" published: "2023-10-27T08:40:00Z" -modified: "2026-06-19T08:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 10 canonicalize: true --- diff --git a/services/console/src/content/docs-reference/zh/server-config.mdx b/services/console/src/content/docs-reference/zh/server-config.mdx index 8bd554505..02a98b04c 100644 --- a/services/console/src/content/docs-reference/zh/server-config.mdx +++ b/services/console/src/content/docs-reference/zh/server-config.mdx @@ -3,7 +3,7 @@ title: "服务器配置" description: "为自托管的持续基准测试配置 Bencher API 服务器" heading: "API 服务器配置" published: "2023-10-27T08:40:00Z" -modified: "2026-06-18T00:00:00Z" +modified: "2026-07-08T00:00:00Z" sortOrder: 7 ---