diff --git a/backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json b/backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json deleted file mode 100644 index 99f3606bcd..0000000000 --- a/backend/.sqlx/query-02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT tag, count(*) as count FROM queue WHERE\n scheduled_for <= now() - ('3 seconds')::interval AND running = false\n GROUP BY tag", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "tag", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - null - ] - }, - "hash": "02b516dac764662194db1bc33e365c01f40bae70af3683f1f09748f6020f0d49" -} diff --git a/backend/.sqlx/query-1f5f0858909eb5bac63c4e3b1add95226bd94ca3facb92a620ffa59dacad6705.json b/backend/.sqlx/query-1f5f0858909eb5bac63c4e3b1add95226bd94ca3facb92a620ffa59dacad6705.json new file mode 100644 index 0000000000..0adff2fe2c --- /dev/null +++ b/backend/.sqlx/query-1f5f0858909eb5bac63c4e3b1add95226bd94ca3facb92a620ffa59dacad6705.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO autoscaling_event (worker_group, event_type, desired_workers, reason) VALUES ($1, $2, $3, $4)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "autoscaling_event_type", + "kind": { + "Enum": [ + "full_scaleout", + "scalein", + "scaleout" + ] + } + } + }, + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "1f5f0858909eb5bac63c4e3b1add95226bd94ca3facb92a620ffa59dacad6705" +} diff --git a/backend/.sqlx/query-4eca060026a0cb19c5794cd56ace89fc04765191f251945d14bbe78718714f6e.json b/backend/.sqlx/query-4eca060026a0cb19c5794cd56ace89fc04765191f251945d14bbe78718714f6e.json new file mode 100644 index 0000000000..3b812a53db --- /dev/null +++ b/backend/.sqlx/query-4eca060026a0cb19c5794cd56ace89fc04765191f251945d14bbe78718714f6e.json @@ -0,0 +1,39 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT event_type::AUTOSCALING_EVENT_TYPE AS \"event_type: _\", EXTRACT(EPOCH FROM (NOW() - applied_at))::int as seconds_ago FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "event_type: _", + "type_info": { + "Custom": { + "name": "autoscaling_event_type", + "kind": { + "Enum": [ + "full_scaleout", + "scalein", + "scaleout" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "seconds_ago", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + null + ] + }, + "hash": "4eca060026a0cb19c5794cd56ace89fc04765191f251945d14bbe78718714f6e" +} diff --git a/backend/.sqlx/query-6d134b137ae81534e145fc5b6474cf963ee26a3ad3a0a3d8dc064cb14c8fd9a6.json b/backend/.sqlx/query-6d134b137ae81534e145fc5b6474cf963ee26a3ad3a0a3d8dc064cb14c8fd9a6.json new file mode 100644 index 0000000000..03351cdeab --- /dev/null +++ b/backend/.sqlx/query-6d134b137ae81534e145fc5b6474cf963ee26a3ad3a0a3d8dc064cb14c8fd9a6.json @@ -0,0 +1,52 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, worker_group, event_type::text, desired_workers, reason, applied_at FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 5", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "worker_group", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "event_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "desired_workers", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "reason", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "applied_at", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + null, + false, + true, + false + ] + }, + "hash": "6d134b137ae81534e145fc5b6474cf963ee26a3ad3a0a3d8dc064cb14c8fd9a6" +} diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 7a8da6eea7..0d39293912 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -10426,6 +10426,7 @@ dependencies = [ "uuid 1.11.0", "windmill-api", "windmill-api-client", + "windmill-autoscaling", "windmill-common", "windmill-git-sync", "windmill-indexer", @@ -10549,6 +10550,21 @@ dependencies = [ "windmill-common", ] +[[package]] +name = "windmill-autoscaling" +version = "1.412.0" +dependencies = [ + "anyhow", + "rsmq_async", + "serde", + "serde_json", + "sqlx", + "tracing", + "uuid 1.11.0", + "windmill-common", + "windmill-queue", +] + [[package]] name = "windmill-common" version = "1.412.0" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 3aa4965c23..700810d20a 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -13,6 +13,7 @@ members = [ "./windmill-common", "./windmill-audit", "./windmill-git-sync", + "./windmill-autoscaling", "./windmill-indexer", "./windmill-macros", "./parsers/windmill-parser", @@ -45,7 +46,7 @@ lto = "thin" [features] default = [] -enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise", "windmill-indexer/enterprise"] +enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise", "windmill-indexer/enterprise"] enterprise_saml = ["windmill-api/enterprise_saml"] stripe = ["windmill-api/stripe"] benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark", "windmill-common/benchmark"] @@ -72,6 +73,7 @@ windmill-git-sync.workspace = true windmill-api = { workspace = true, default-features = false } windmill-worker.workspace = true windmill-indexer = { workspace = true, optional = true } +windmill-autoscaling = { workspace = true, optional = true } futures.workspace = true tracing.workspace = true sqlx.workspace = true @@ -116,6 +118,7 @@ windmill-worker = { path = "./windmill-worker" } windmill-common = { path = "./windmill-common", default-features = false } windmill-audit = { path = "./windmill-audit" } windmill-git-sync = { path = "./windmill-git-sync" } +windmill-autoscaling = { path = "./windmill-autoscaling" } windmill-indexer = {path = "./windmill-indexer"} windmill-macros = {path = "./windmill-macros"} windmill-parser = { path = "./parsers/windmill-parser" } diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 0b60c124b1..07ac755375 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -d61c163e0a311ecd86d1398aff883eaea8d0b09a +816abd18b24831f251e11d44d274c8acef89df12 \ No newline at end of file diff --git a/backend/migrations/20241024125924_autoscaling.down.sql b/backend/migrations/20241024125924_autoscaling.down.sql new file mode 100644 index 0000000000..8f536a7553 --- /dev/null +++ b/backend/migrations/20241024125924_autoscaling.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here +DROP TABLE autoscaling_event; +DROP TYPE autoscaling_event_type; diff --git a/backend/migrations/20241024125924_autoscaling.up.sql b/backend/migrations/20241024125924_autoscaling.up.sql new file mode 100644 index 0000000000..fa3b161d56 --- /dev/null +++ b/backend/migrations/20241024125924_autoscaling.up.sql @@ -0,0 +1,13 @@ +-- Add up migration script here +CREATE TYPE AUTOSCALING_EVENT_TYPE AS ENUM ('full_scaleout', 'scalein', 'scaleout'); + +CREATE TABLE autoscaling_event ( + id SERIAL PRIMARY KEY, + worker_group TEXT NOT NULL, + event_type AUTOSCALING_EVENT_TYPE NOT NULL, + desired_workers INTEGER NOT NULL, + applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + reason TEXT +); + +CREATE INDEX autoscaling_event_worker_group_idx ON autoscaling_event (worker_group, applied_at); \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index 84deb4e6b5..f240961734 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -548,8 +548,11 @@ Windmill Community Edition {GIT_VERSION} rx.recv().await?; } } - tracing::info!("Starting phase 2 of shutdown"); - killpill_phase2_tx.send(())?; + if killpill_phase2_tx.receiver_count() > 0 { + tracing::info!("Starting phase 2 of shutdown"); + killpill_phase2_tx.send(())?; + tracing::info!("Phase 2 of shutdown completed"); + } Ok(()) as anyhow::Result<()> }; diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 576a38de72..17dd833d62 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -27,7 +27,7 @@ use windmill_api::{ DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN, }; #[cfg(feature = "enterprise")] -use windmill_common::ee::{worker_groups_alerts, jobs_waiting_alerts}; +use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts}; use windmill_common::{ auth::JWT_SECRET, ee::CriticalErrorChannel, @@ -1068,6 +1068,15 @@ pub async fn monitor_db( } }; + let apply_autoscaling_f = async { + #[cfg(feature = "enterprise")] + if server_mode && !initial_load { + if let Err(e) = windmill_autoscaling::apply_all_autoscaling(db).await { + tracing::error!("Error applying autoscaling: {:?}", e); + } + } + }; + join!( expired_items_f, zombie_jobs_f, @@ -1075,6 +1084,7 @@ pub async fn monitor_db( verify_license_key_f, worker_groups_alerts_f, jobs_waiting_alerts_f, + apply_autoscaling_f, ); } @@ -1092,19 +1102,11 @@ pub async fn expose_queue_metrics(db: &Pool) { .unwrap_or(true); if metrics_enabled || save_metrics { - let queue_counts = sqlx::query!( - "SELECT tag, count(*) as count FROM queue WHERE - scheduled_for <= now() - ('3 seconds')::interval AND running = false - GROUP BY tag" - ) - .fetch_all(db) - .await - .ok() - .unwrap_or_else(|| vec![]); + let queue_counts = windmill_common::queue::get_queue_counts(db).await; for q in queue_counts { - let count = q.count.unwrap_or(0); - let tag = q.tag; + let count = q.1; + let tag = q.0; if metrics_enabled { let metric = (*QUEUE_COUNT).with_label_values(&[&tag]); metric.set(count as i64); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 366f26ccc0..e854bff21d 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -8198,6 +8198,29 @@ paths: items: $ref: "#/components/schemas/Config" + /configs/list_autoscaling_events/{worker_group}: + get: + summary: List autoscaling events + operationId: listAutoscalingEvents + tags: + - config + parameters: + - name: worker_group + in: path + required: true + schema: + type: string + responses: + "200": + description: List of autoscaling events + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/AutoscalingEvent" + + /w/{workspace}/acls/get/{kind}/{path}: get: summary: get granular acls @@ -12469,3 +12492,21 @@ components: properties: dancer: type: string + + AutoscalingEvent: + type: object + properties: + id: + type: integer + format: int64 + worker_group: + type: string + event_type: + type: string + desired_workers: + type: integer + reason: + type: string + applied_at: + type: string + format: date-time diff --git a/backend/windmill-api/src/configs.rs b/backend/windmill-api/src/configs.rs index 64c8a1884f..81b65216e0 100644 --- a/backend/windmill-api/src/configs.rs +++ b/backend/windmill-api/src/configs.rs @@ -29,6 +29,10 @@ pub fn global_service() -> Router { .route("/update/:name", post(update_config).delete(delete_config)) .route("/get/:name", get(get_config)) .route("/list", get(list_configs)) + .route( + "/list_autoscaling_events/:worker_group", + get(list_autoscaling_events), + ) } #[derive(Serialize, Deserialize, FromRow)] @@ -177,6 +181,30 @@ async fn delete_config( Ok(format!("Deleted config {name}")) } +#[derive(Serialize, Deserialize, FromRow)] +struct AutoscalingEvent { + id: i64, + worker_group: String, + event_type: Option, + desired_workers: i32, + reason: Option, + applied_at: chrono::NaiveDateTime, +} + +async fn list_autoscaling_events( + Extension(db): Extension, + Path(worker_group): Path, +) -> error::JsonResult> { + let events = sqlx::query_as!( + AutoscalingEvent, + "SELECT id, worker_group, event_type::text, desired_workers, reason, applied_at FROM autoscaling_event WHERE worker_group = $1 ORDER BY applied_at DESC LIMIT 5", + worker_group + ) + .fetch_all(&db) + .await?; + Ok(Json(events)) +} + #[cfg(feature = "enterprise")] async fn list_configs( authed: ApiAuthed, diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 53cd958c6f..7244b2be17 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -2815,7 +2815,6 @@ pub async fn run_flow_by_path_inner( let flow_path = flow_path.to_path(); check_scopes(&authed, || format!("run:flow/{flow_path}"))?; - let (tag, dedicated_worker, has_preprocessor) = sqlx::query!( "SELECT tag, dedicated_worker, flow_version.value->>'preprocessor_module' IS NOT NULL as has_preprocessor FROM flow @@ -2910,7 +2909,6 @@ pub async fn restart_flow( ) -> error::Result<(StatusCode, String)> { check_license_key_valid().await?; - let completed_job = sqlx::query_as::<_, CompletedJob>( "SELECT *, result->'wm_labels' as labels from completed_job WHERE id = $1 and workspace_id = $2", ) @@ -3010,7 +3008,6 @@ pub async fn run_script_by_path_inner( check_scopes(&authed, || format!("run:script/{script_path}"))?; - let (job_payload, tag, _delete_after_use, timeout) = script_path_to_payload(script_path, &db, &w_id, run_query.skip_preprocessor).await?; let scheduled_for = run_query.get_scheduled_for(&db).await?; @@ -3018,7 +3015,6 @@ pub async fn run_script_by_path_inner( let tag = run_query.tag.clone().or(tag); check_tag_available_for_workspace(&w_id, &tag).await?; - let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq); let (uuid, tx) = push( @@ -3067,7 +3063,6 @@ pub async fn run_workflow_as_code( Query(wkflow_query): Query, Json(task): Json, ) -> error::Result<(StatusCode, String)> { - let mut i = 1; if *CLOUD_HOSTED { @@ -3079,23 +3074,18 @@ pub async fn run_workflow_as_code( check_license_key_valid().await?; check_tag_available_for_workspace(&w_id, &run_query.tag).await?; - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; } - let job = get_queued_job(&job_id, &w_id, &db).await?; - - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; } - let job = not_found_if_none(job, "Queued Job", &job_id.to_string())?; let (job_payload, tag, _delete_after_use, timeout) = match job.job_kind { JobKind::Preview => ( @@ -3118,18 +3108,12 @@ pub async fn run_workflow_as_code( run_query.timeout, ), JobKind::Script => { - script_path_to_payload( - job.script_path(), - &db, - &w_id, - run_query.skip_preprocessor, - ) - .await? + script_path_to_payload(job.script_path(), &db, &w_id, run_query.skip_preprocessor) + .await? } _ => return Err(anyhow::anyhow!("Not supported").into()), }; - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; @@ -3143,22 +3127,18 @@ pub async fn run_workflow_as_code( let tag = run_query.tag.clone().or(tag).or(Some(job.tag)); - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; } - let tx = PushIsolationLevel::Isolated(user_db, authed.clone().into(), rsmq); - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; } - let (uuid, mut tx) = push( &db, tx, @@ -3185,7 +3165,6 @@ pub async fn run_workflow_as_code( ) .await?; - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; @@ -3203,16 +3182,13 @@ pub async fn run_workflow_as_code( tracing::info!("Skipping update of flow status for job {job_id} in workspace {w_id}"); } - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); i += 1; } - tx.commit().await?; - if *CLOUD_HOSTED { tracing::info!("workflow_as_code_tracing id {i} "); } @@ -3866,7 +3842,6 @@ pub async fn run_wait_result_flow_by_path_internal( let flow_path = flow_path.to_path(); check_scopes(&authed, || format!("run:flow/{flow_path}"))?; - let scheduled_for = run_query.get_scheduled_for(&db).await?; let (tag, dedicated_worker, early_return, has_preprocessor) = sqlx::query!( @@ -4362,7 +4337,6 @@ async fn add_batch_jobs( } } "flow" => { - let mut uuids: Vec = Vec::new(); let payload = if let Some(ref fv) = batch_info.flow_value { JobPayload::RawFlow { value: fv.clone(), path: None, restarted_from: None } @@ -4580,7 +4554,6 @@ pub async fn run_job_by_hash_inner( #[cfg(feature = "enterprise")] check_license_key_valid().await?; - let hash = script_hash.0; let ( path, diff --git a/backend/windmill-autoscaling/Cargo.toml b/backend/windmill-autoscaling/Cargo.toml new file mode 100644 index 0000000000..7aada6f904 --- /dev/null +++ b/backend/windmill-autoscaling/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "windmill-autoscaling" +version.workspace = true +authors.workspace = true +edition.workspace = true + +[lib] +name = "windmill_autoscaling" +path = "./src/lib.rs" + +[features] +enterprise = ["windmill-queue/enterprise", "windmill-common/enterprise"] +default = [] + +[dependencies] +uuid.workspace = true +serde.workspace = true +sqlx.workspace = true +serde_json.workspace = true +tracing.workspace = true +windmill-common = { workspace = true, default-features = false } +windmill-queue.workspace = true +rsmq_async.workspace = true +anyhow.workspace = true \ No newline at end of file diff --git a/backend/windmill-autoscaling/src/autoscaling_ee.rs b/backend/windmill-autoscaling/src/autoscaling_ee.rs new file mode 100644 index 0000000000..1c9defbede --- /dev/null +++ b/backend/windmill-autoscaling/src/autoscaling_ee.rs @@ -0,0 +1,6 @@ +use windmill_common::DB; + +pub async fn apply_all_autoscaling(_db: &DB) -> anyhow::Result<()> { + // Autoscaling is an ee feature + Ok(()) +} diff --git a/backend/windmill-autoscaling/src/lib.rs b/backend/windmill-autoscaling/src/lib.rs new file mode 100644 index 0000000000..28b9319244 --- /dev/null +++ b/backend/windmill-autoscaling/src/lib.rs @@ -0,0 +1,2 @@ +mod autoscaling_ee; +pub use autoscaling_ee::*; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index 0f25e12e46..bcac7b85fb 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -17,6 +17,7 @@ use scripts::ScriptLang; use sqlx::{Pool, Postgres}; pub mod apps; +pub mod auth; #[cfg(feature = "benchmark")] pub mod bench; pub mod db; @@ -32,9 +33,8 @@ pub mod job_s3_helpers_ee; pub mod jobs; pub mod more_serde; pub mod oauth2; +pub mod queue; pub mod s3_helpers; - -pub mod auth; pub mod schedule; pub mod scripts; pub mod server; diff --git a/backend/windmill-common/src/queue.rs b/backend/windmill-common/src/queue.rs new file mode 100644 index 0000000000..6cbb5611c5 --- /dev/null +++ b/backend/windmill-common/src/queue.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +use sqlx::{Pool, Postgres}; + +pub async fn get_queue_counts(db: &Pool) -> HashMap { + sqlx::query_as::<_, (String, i64)>( + "SELECT tag, count(*) as count FROM queue WHERE + scheduled_for <= now() - ('3 seconds')::interval AND running = false + GROUP BY tag", + ) + .fetch_all(db) + .await + .ok() + .map(|v| v.into_iter().map(|(k, v)| (k, v as u32)).collect()) + .unwrap_or_else(|| HashMap::new()) +} diff --git a/backend/windmill-worker/src/pg_executor.rs b/backend/windmill-worker/src/pg_executor.rs index 7702b73b03..f27eeee12b 100644 --- a/backend/windmill-worker/src/pg_executor.rs +++ b/backend/windmill-worker/src/pg_executor.rs @@ -85,7 +85,7 @@ fn do_postgresql_inner<'a>( let arg_t = arg .otyp .as_ref() - .ok_or_else(|| anyhow::anyhow!("Missing otyp for pg arg"))?; + .ok_or_else(|| anyhow::anyhow!("Missing otzyp for pg arg"))?; let typ = &arg.typ; let param = convert_val(value, arg_t, typ)?; query_params.push(param); diff --git a/frontend/src/lib/components/AutoscalingConfigEditor.svelte b/frontend/src/lib/components/AutoscalingConfigEditor.svelte new file mode 100644 index 0000000000..3f763d835e --- /dev/null +++ b/frontend/src/lib/components/AutoscalingConfigEditor.svelte @@ -0,0 +1,364 @@ + + +
+
+
Rules
+ { + dispatch('dirty') + if (e.detail) { + if (!config) { + config = { + enabled: true, + min_workers: 3, + max_workers: 10, + integration: { type: 'dryrun' } + } + } else { + config.enabled = true + } + } else { + config = { + ...(config ?? { + min_workers: 3, + max_workers: 10, + integration: { type: 'dryrun' } + }), + enabled: false + } + } + }} + /> + + + + +
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+
Integration
+ {#if config?.integration} + dispatch('dirty')} + bind:selected={config.integration.type} + class="mb-4 mt-2" + > + + + + + + + + {#if config.integration.type === 'script'} + + + + +
+ + +
+
+ +
+ + workers +
+
+ {/if} + {:else} + + + + + + + + + + {/if} +
+
diff --git a/frontend/src/lib/components/AutoscalingEvents.svelte b/frontend/src/lib/components/AutoscalingEvents.svelte new file mode 100644 index 0000000000..36240cef4c --- /dev/null +++ b/frontend/src/lib/components/AutoscalingEvents.svelte @@ -0,0 +1,73 @@ + + +
+
Autoscaling events {#if $enterpriseLicense}(5 last) + +
+ {#if !$enterpriseLicense} +
Autoscaling is an EE feature
+ {:else if loading} + + {:else if events} + {#if events.length == 0} +
No events, is autoscaling set in the worker group config?
+ {:else} +
+ {#each events as event} +
+
{event.event_type} to {event.desired_workers}
+
{event.reason}
+
+
+ {/each} +
+ {/if} + {/if} +
diff --git a/frontend/src/lib/components/InstanceSettings.svelte b/frontend/src/lib/components/InstanceSettings.svelte index 3b42bd0e10..c924fc8b3d 100644 --- a/frontend/src/lib/components/InstanceSettings.svelte +++ b/frontend/src/lib/components/InstanceSettings.svelte @@ -277,7 +277,9 @@
Setting SMTP unlocks sending emails upon adding new users to the workspace or the instance or sending critical alerts. - Learn moreLearn more
{:else if category == 'Registries'} diff --git a/frontend/src/lib/components/OAuthSetting.svelte b/frontend/src/lib/components/OAuthSetting.svelte index e2fe394761..8277ddf396 100644 --- a/frontend/src/lib/components/OAuthSetting.svelte +++ b/frontend/src/lib/components/OAuthSetting.svelte @@ -66,7 +66,7 @@ > {#if enabled}
- {#if name != "slack"} + {#if name != 'slack'}