Skip to content

Commit dd6b7b7

Browse files
Merge pull request #3135 from fibonacci1729/limit-mem-usage
Add support for limiting instance memory usage in spin up
2 parents 4223521 + 1b7430b commit dd6b7b7

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

crates/runtime-config/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub struct ResolvedRuntimeConfig<T> {
4545
///
4646
/// `None` is used for an "unset" log directory.
4747
pub log_dir: Option<PathBuf>,
48+
/// The maximum memory allocation limit.
49+
pub max_instance_memory: Option<usize>,
4850
/// The input TOML, for informational summaries.
4951
pub toml: toml::Table,
5052
}
@@ -137,12 +139,15 @@ where
137139

138140
let toml = toml_resolver.toml();
139141
let log_dir = toml_resolver.log_dir()?;
142+
let max_instance_memory = toml_resolver.max_instance_memory()?;
143+
140144
let source = TomlRuntimeConfigSource::new(
141145
toml_resolver,
142146
&key_value_resolver,
143147
tls_resolver.as_ref(),
144148
&sqlite_resolver,
145149
);
150+
146151
// Note: all valid fields in the runtime config must have been referenced at
147152
// this point or the finalizer will fail due to `validate_all_keys_used`
148153
// not passing.
@@ -154,6 +159,7 @@ where
154159
sqlite_resolver,
155160
state_dir,
156161
log_dir,
162+
max_instance_memory,
157163
toml,
158164
})
159165
}
@@ -167,6 +173,11 @@ where
167173
pub fn log_dir(&self) -> Option<PathBuf> {
168174
self.log_dir.clone()
169175
}
176+
177+
/// The maximum memory allocation limit.
178+
pub fn max_instance_memory(&self) -> Option<usize> {
179+
self.max_instance_memory
180+
}
170181
}
171182

172183
#[derive(Clone, Debug)]
@@ -261,6 +272,16 @@ impl<'a> TomlResolver<'a> {
261272
}
262273
}
263274

275+
/// Get the configured maximum memory allocation limit.
276+
pub fn max_instance_memory(&self) -> anyhow::Result<Option<usize>> {
277+
self.table
278+
.get("max_instance_memory")
279+
.and_then(|v| v.as_integer())
280+
.map(|toml_value| toml_value.try_into())
281+
.transpose()
282+
.map_err(Into::into)
283+
}
284+
264285
/// Validate that all keys in the TOML file have been used.
265286
pub fn validate_all_keys_used(&self) -> spin_factors::Result<()> {
266287
self.table.validate_all_keys_used()

crates/runtime-factors/src/build.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use anyhow::Context as _;
66
use spin_factors_executor::FactorsExecutor;
77
use spin_runtime_config::ResolvedRuntimeConfig;
88
use spin_trigger::cli::{
9-
FactorsConfig, InitialKvSetterHook, KeyValueDefaultStoreSummaryHook, RuntimeFactorsBuilder,
10-
SqlStatementExecutorHook, SqliteDefaultStoreSummaryHook, StdioLoggingExecutorHooks,
9+
FactorsConfig, InitialKvSetterHook, KeyValueDefaultStoreSummaryHook, MaxInstanceMemoryHook,
10+
RuntimeFactorsBuilder, SqlStatementExecutorHook, SqliteDefaultStoreSummaryHook,
11+
StdioLoggingExecutorHooks,
1112
};
1213

1314
/// A [`RuntimeFactorsBuilder`] for [`TriggerFactors`].
@@ -56,6 +57,16 @@ impl RuntimeFactorsBuilder for FactorsBuilder {
5657
executor.add_hooks(InitialKvSetterHook::new(args.key_values.clone()));
5758
executor.add_hooks(SqliteDefaultStoreSummaryHook);
5859
executor.add_hooks(KeyValueDefaultStoreSummaryHook);
60+
61+
let max_instance_memory = args
62+
.max_instance_memory
63+
.or(runtime_config.max_instance_memory());
64+
65+
// Only add the hook if a max instance memory size is specified via flag or runtime config.
66+
if let Some(max_instance_memory) = max_instance_memory {
67+
executor.add_hooks(MaxInstanceMemoryHook::new(max_instance_memory));
68+
}
69+
5970
Ok(())
6071
}
6172
}

crates/runtime-factors/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ pub struct TriggerAppArgs {
100100
/// To run from a file, prefix the filename with @ e.g. spin up --sqlite @migration.sql
101101
#[clap(long = "sqlite")]
102102
pub sqlite_statements: Vec<String>,
103+
104+
/// Sets the maxmimum memory allocation limit for an instance in bytes.
105+
#[clap(long, env = "SPIN_MAX_INSTANCE_MEMORY")]
106+
pub max_instance_memory: Option<usize>,
103107
}
104108

105109
impl From<ResolvedRuntimeConfig<TriggerFactorsRuntimeConfig>> for TriggerFactorsRuntimeConfig {

crates/trigger/src/cli.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod initial_kv_setter;
22
mod launch_metadata;
3+
mod max_instance_memory;
34
mod sqlite_statements;
45
mod stdio;
56
mod summary;
@@ -19,6 +20,7 @@ use spin_factors_executor::{ComponentLoader, FactorsExecutor};
1920
use crate::{loader::ComponentLoader as ComponentLoaderImpl, Trigger, TriggerApp};
2021
pub use initial_kv_setter::InitialKvSetterHook;
2122
pub use launch_metadata::LaunchMetadata;
23+
pub use max_instance_memory::MaxInstanceMemoryHook;
2224
pub use sqlite_statements::SqlStatementExecutorHook;
2325
use stdio::FollowComponents;
2426
pub use stdio::StdioLoggingExecutorHooks;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use spin_core::async_trait;
2+
use spin_factors::RuntimeFactors;
3+
use spin_factors_executor::{ExecutorHooks, FactorsInstanceBuilder};
4+
5+
/// An [`ExecutorHooks`] that sets the maximum memory allocation limit.
6+
pub struct MaxInstanceMemoryHook {
7+
max_instance_memory: usize,
8+
}
9+
10+
impl MaxInstanceMemoryHook {
11+
pub fn new(max_instance_memory: usize) -> Self {
12+
Self {
13+
max_instance_memory,
14+
}
15+
}
16+
}
17+
18+
#[async_trait]
19+
impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for MaxInstanceMemoryHook {
20+
fn prepare_instance(&self, builder: &mut FactorsInstanceBuilder<F, U>) -> anyhow::Result<()> {
21+
builder
22+
.store_builder()
23+
.max_memory_size(self.max_instance_memory);
24+
Ok(())
25+
}
26+
}

0 commit comments

Comments
 (0)