Symptom
An HTTP endpoint backed by a function returns in 200 ms most of the time, but the first request after a quiet period takes several seconds. Event-driven functions are fine; user-facing ones aren't.
1. Measure
- Function metrics (
oci_faasnamespace):FunctionInvocationCount,FunctionExecutionDuration,FunctionResponseCountby status. - API Gateway metrics and access logs: latency percentiles per route.
- Compare p50 vs p99 latency; a large gap at low traffic is typically cold starts.
2. Make start-up cheaper
| Cause | Fix |
|---|---|
| Large container image | Slim base images (FDK images), remove build tools, multi-stage builds |
| Heavy initialisation per call | Initialise SDK clients and config once (outside the handler) |
| Big language runtimes/frameworks | Lighter frameworks; Java: consider GraalVM native images where supported |
| Fetching secrets on every call | Cache with a TTL inside the container |
| Image pulled from a far region | Keep OCIR repository in the same region as the function |
Image size has a direct effect on cold start time. Check it in OCIR and aim to shrink it.
3. Size memory
Functions are configured with a memory limit (128 MB up to the service maximum), and CPU share follows memory. Too little memory slows CPU-bound code and start-up, and too much costs more per second. Test a few sizes with realistic payloads:
fn update function orders-app enrich-order --memory 512
fn invoke orders-app enrich-order < sample.json
Billing is based on memory × execution time (GB-seconds) plus invocations, so faster at a bigger size can cost the same or less.
4. Provisioned concurrency for latency-sensitive functions
Provisioned concurrency keeps a number of function instances ready, which removes cold starts for that capacity. It's billed for as long as it's configured. Set it on the function (console, CLI or Terraform) in provisioned concurrency units, sized to typical concurrent requests:
oci fn function update --function-id $FN \
--provisioned-concurrency '{"strategy": "CONSTANT", "count": 20}'
Check the units and minimums in the current docs. Use it only for user-facing paths, and leave background functions on demand.
5. Timeouts and invocation patterns
- Timeout close to real p99 duration (not the maximum "just in case").
- For long work, use detached (asynchronous) invocation or move it to a container instance/job.
- Batch events where possible instead of one invocation per tiny event.
- API Gateway caching for identical GET requests.
6. Watch concurrency limits
Functions have tenancy/application limits on concurrent memory. A spike in one function can throttle others in the same application. Separate noisy functions into their own application and request limit increases ahead of known peaks.
7. Track it
| Metric | Before | After |
|---|---|---|
| API p99 latency | ||
| Image size | ||
| Average duration and memory | ||
| Monthly Functions cost |
Tip
If one function serves a public API and the rest process events, give the API function its own application with provisioned concurrency. Event processors keep the pay-per-use model.
Checklist
- Cold starts identified (p99 vs p50, low-traffic periods)
- Image size and initialisation reduced
- Memory tested at several sizes
- Provisioned concurrency only on latency-sensitive functions
- Timeouts, async patterns and batching reviewed
- Concurrency limits checked