Add AICL example: 34_serverless_platform.aicl
Browse files
data/aicl/examples/34_serverless_platform.aicl
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AICL Example: Serverless Platform
|
| 2 |
+
# Implements a serverless/function-as-a-service platform with function runtime management,
|
| 3 |
+
# cold start optimization, event triggers, auto-scaling, billing, and execution isolation.
|
| 4 |
+
|
| 5 |
+
# Level 1: Architecture
|
| 6 |
+
Goal: Provide an elastic serverless compute platform that executes user-defined functions in response to events with sub-second cold start times, automatic scaling from zero to thousands of concurrent invocations, per-invocation billing, and secure multi-tenant isolation.
|
| 7 |
+
|
| 8 |
+
Constraint: Cold start latency must not exceed 500ms for interpreted runtimes and 2s for compiled runtimes
|
| 9 |
+
Constraint: Function execution timeout must be strictly enforced at configured limit (max 15 minutes)
|
| 10 |
+
Constraint: Concurrent invocations per function must scale from 0 to 1000 within 60 seconds
|
| 11 |
+
Constraint: Billing must be metered per invocation with 1ms granularity
|
| 12 |
+
Constraint: Function isolation must prevent cross-tenant data access and resource interference
|
| 13 |
+
|
| 14 |
+
Risk: Cold start latency spikes causing SLA violations during traffic bursts
|
| 15 |
+
Recovery: Maintain warm execution pool with pre-initialized containers; predictive pre-warming based on traffic patterns; reserved concurrency for latency-sensitive functions; snapshot-based restore for sub-100ms cold starts
|
| 16 |
+
|
| 17 |
+
Risk: Noisy neighbor causing performance degradation for co-located functions
|
| 18 |
+
Recovery: Strict resource isolation via Firecracker microVMs; per-function CPU and memory limits enforced by cgroups; network bandwidth shaping; dedicated worker pools for premium functions
|
| 19 |
+
|
| 20 |
+
Risk: Event source failure causing missed function invocations
|
| 21 |
+
Recovery: At-least-once delivery with idempotent execution; dead-letter queue for failed invocations; event source retry with exponential backoff; checkpoint-based offset tracking for stream sources
|
| 22 |
+
|
| 23 |
+
Risk: Runaway function execution consuming excessive resources
|
| 24 |
+
Recovery: Hard execution timeout enforced at runtime level; memory limit with OOM kill; CPU throttling via cgroups; per-function concurrent invocation cap; automatic suspension after repeated failures
|
| 25 |
+
|
| 26 |
+
Risk: Billing metering inaccuracy from distributed counter drift
|
| 27 |
+
Recovery: Atomic timestamped execution records; dual-path metering (real-time estimate + reconciled billing); periodic reconciliation with audit trail; per-invocation billing records persisted before response
|
| 28 |
+
|
| 29 |
+
Risk: Security breach via function escape to host system
|
| 30 |
+
Recovery: Defense in depth: microVM isolation, seccomp, AppArmor, read-only filesystem, non-root execution, network policies, and regular CVE patching; immediate quarantine on detection
|
| 31 |
+
|
| 32 |
+
Layer: FunctionRuntime
|
| 33 |
+
SubLayer: ContainerPool
|
| 34 |
+
SubLayer: ColdStartOptimizer
|
| 35 |
+
SubLayer: ExecutionEngine
|
| 36 |
+
SubLayer: ResourceEnforcer
|
| 37 |
+
Layer: EventIngestion
|
| 38 |
+
SubLayer: TriggerManager
|
| 39 |
+
SubLayer: EventQueue
|
| 40 |
+
SubLayer: DLQProcessor
|
| 41 |
+
Layer: ScalingController
|
| 42 |
+
SubLayer: ConcurrencyManager
|
| 43 |
+
SubLayer: WorkerProvisioner
|
| 44 |
+
SubLayer: ScalePredictor
|
| 45 |
+
Layer: BillingMetering
|
| 46 |
+
SubLayer: ExecutionRecorder
|
| 47 |
+
SubLayer: UsageAggregator
|
| 48 |
+
SubLayer: InvoiceGenerator
|
| 49 |
+
|
| 50 |
+
Validation: Function deployment package must not exceed 250MB uncompressed
|
| 51 |
+
Constraint: Each function invocation must have a unique invocation ID for tracing
|
| 52 |
+
Validation: Reserved concurrency must not exceed account-level concurrency limit
|
| 53 |
+
Validation: Event trigger configurations must be validated before activation
|
| 54 |
+
Validation: Execution timeout must be enforced even if function code ignores signals
|
| 55 |
+
Validation: Billing records must be immutable once written
|
| 56 |
+
|
| 57 |
+
# Level 2: Entities
|
| 58 |
+
Entity Function
|
| 59 |
+
functionId: string
|
| 60 |
+
functionName: string
|
| 61 |
+
runtime: string
|
| 62 |
+
handler: string
|
| 63 |
+
codeSize: integer
|
| 64 |
+
memorySize: integer
|
| 65 |
+
timeout: integer
|
| 66 |
+
reservedConcurrency: integer
|
| 67 |
+
environment: dict
|
| 68 |
+
layers: list
|
| 69 |
+
lastModified: datetime
|
| 70 |
+
version: string
|
| 71 |
+
|
| 72 |
+
Entity Invocation
|
| 73 |
+
invocationId: string
|
| 74 |
+
functionId: string
|
| 75 |
+
triggerType: string
|
| 76 |
+
startTime: datetime
|
| 77 |
+
endTime: datetime
|
| 78 |
+
duration: float
|
| 79 |
+
memoryUsed: integer
|
| 80 |
+
billedDuration: float
|
| 81 |
+
status: string
|
| 82 |
+
errorType: string
|
| 83 |
+
requestId: string
|
| 84 |
+
logOutput: string
|
| 85 |
+
|
| 86 |
+
Entity ExecutionEnvironment
|
| 87 |
+
envId: string
|
| 88 |
+
functionId: string
|
| 89 |
+
workerNode: string
|
| 90 |
+
state: string
|
| 91 |
+
createdAt: datetime
|
| 92 |
+
lastInvocation: datetime
|
| 93 |
+
invocationCount: integer
|
| 94 |
+
memoryAllocation: integer
|
| 95 |
+
isActive: boolean
|
| 96 |
+
snapshotId: string
|
| 97 |
+
|
| 98 |
+
Entity EventTrigger
|
| 99 |
+
triggerId: string
|
| 100 |
+
functionId: string
|
| 101 |
+
triggerType: string
|
| 102 |
+
sourceArn: string
|
| 103 |
+
batchSize: integer
|
| 104 |
+
maximumBatchingWindow: integer
|
| 105 |
+
filterPattern: dict
|
| 106 |
+
enabled: boolean
|
| 107 |
+
lastTriggered: datetime
|
| 108 |
+
failedInvocations: integer
|
| 109 |
+
|
| 110 |
+
Entity ConcurrencyState
|
| 111 |
+
functionId: string
|
| 112 |
+
reservedConcurrency: integer
|
| 113 |
+
availableConcurrency: integer
|
| 114 |
+
activeInvocations: integer
|
| 115 |
+
pendingInvocations: integer
|
| 116 |
+
throttleCount: integer
|
| 117 |
+
scaleTargetConcurrency: integer
|
| 118 |
+
lastScaleTime: datetime
|
| 119 |
+
|
| 120 |
+
Entity BillingRecord
|
| 121 |
+
recordId: string
|
| 122 |
+
accountId: string
|
| 123 |
+
functionId: string
|
| 124 |
+
invocationId: string
|
| 125 |
+
startTime: datetime
|
| 126 |
+
endTime: datetime
|
| 127 |
+
durationMs: integer
|
| 128 |
+
memoryMB: integer
|
| 129 |
+
computeCharge: float
|
| 130 |
+
requestCharge: float
|
| 131 |
+
region: string
|
| 132 |
+
writtenAt: datetime
|
| 133 |
+
|
| 134 |
+
# Level 3: Behaviors
|
| 135 |
+
Behavior InvokeFunction
|
| 136 |
+
Input: functionId: string, payload: bytes, invocationType: string
|
| 137 |
+
Output: response: bytes, invocationId: string, duration: float, billedDuration: float
|
| 138 |
+
Action:
|
| 139 |
+
Validate function exists and is in active state
|
| 140 |
+
Check concurrency limits; throttle if exceeded
|
| 141 |
+
Acquire execution environment from warm pool or create new
|
| 142 |
+
Inject event payload and context into function runtime
|
| 143 |
+
Start execution timer with configured timeout
|
| 144 |
+
Execute function handler with resource limits enforced
|
| 145 |
+
Capture response, logs, and metrics
|
| 146 |
+
Return environment to warm pool if reusable
|
| 147 |
+
Record billing with millisecond granularity
|
| 148 |
+
Return response or error with invocation ID
|
| 149 |
+
|
| 150 |
+
Behavior PreWarmEnvironment
|
| 151 |
+
Input: functionId: string, runtime: string, memorySize: integer, count: integer
|
| 152 |
+
Output: envIds: list, warmTime: float
|
| 153 |
+
Action:
|
| 154 |
+
Predict upcoming demand based on historical patterns
|
| 155 |
+
Provision container with function code and runtime
|
| 156 |
+
Initialize runtime environment (load handler, set env vars)
|
| 157 |
+
Optionally create snapshot for fast restore
|
| 158 |
+
Add to warm pool with availability timestamp
|
| 159 |
+
Track warm pool size against reserved concurrency
|
| 160 |
+
Emit pre-warm metric with provisioning time
|
| 161 |
+
|
| 162 |
+
Behavior ProcessEventTrigger
|
| 163 |
+
Input: triggerId: string, events: list
|
| 164 |
+
Output: invocationIds: list, successCount: integer, failureCount: integer
|
| 165 |
+
Action:
|
| 166 |
+
Validate trigger is enabled and function is active
|
| 167 |
+
Batch events according to trigger configuration
|
| 168 |
+
Apply filter pattern to remove non-matching events
|
| 169 |
+
For each batch, invoke function with event payload
|
| 170 |
+
Handle partial batch failures with retry logic
|
| 171 |
+
Send failed invocations to DLQ after max retries
|
| 172 |
+
Update trigger metrics (success/failure counts)
|
| 173 |
+
Acknowledge event source to advance checkpoint
|
| 174 |
+
|
| 175 |
+
Behavior ScaleConcurrency
|
| 176 |
+
Input: functionId: string, currentLoad: float, targetUtilization: float
|
| 177 |
+
Output: newConcurrency: integer, provisionedWorkers: integer
|
| 178 |
+
Action:
|
| 179 |
+
Measure current concurrent invocations and queue depth
|
| 180 |
+
Calculate desired concurrency based on target utilization
|
| 181 |
+
Respect reserved and account concurrency limits
|
| 182 |
+
If scaling up, provision new execution environments
|
| 183 |
+
If scaling down, allow warm pool to drain naturally
|
| 184 |
+
Implement scale-up cooldown of 30s and scale-down of 300s
|
| 185 |
+
Emit scaling decision metric with reasoning
|
| 186 |
+
|
| 187 |
+
Behavior EnforceTimeout
|
| 188 |
+
Input: invocationId: string, timeoutSeconds: integer
|
| 189 |
+
Output: terminated: boolean, terminationReason: string
|
| 190 |
+
Action:
|
| 191 |
+
Set hard deadline timer at invocation start
|
| 192 |
+
If function completes before deadline, cancel timer
|
| 193 |
+
If deadline reached, send SIGTERM to function process
|
| 194 |
+
Wait 500ms graceful shutdown period
|
| 195 |
+
If still running, send SIGKILL and destroy environment
|
| 196 |
+
Record timeout error in invocation log
|
| 197 |
+
Emit timeout metric with function ID
|
| 198 |
+
|
| 199 |
+
Behavior RecordBilling
|
| 200 |
+
Input: functionId: string, invocationId: string, durationMs: integer, memoryMB: integer
|
| 201 |
+
Output: recordId: string, computeCharge: float, requestCharge: float
|
| 202 |
+
Action:
|
| 203 |
+
Calculate billed duration (rounded up to nearest ms)
|
| 204 |
+
Compute compute charge = durationMs * memoryMB * rate
|
| 205 |
+
Compute request charge per invocation
|
| 206 |
+
Create immutable billing record with timestamp
|
| 207 |
+
Persist to billing store before returning invocation response
|
| 208 |
+
Aggregate hourly usage for account-level reporting
|
| 209 |
+
Emit billing metric with cost breakdown
|
| 210 |
+
|
| 211 |
+
# Level 4: Conditions
|
| 212 |
+
Condition: ConcurrencyLimitReached
|
| 213 |
+
When active invocations equal reserved concurrency for a function
|
| 214 |
+
Then throttle additional invocations with 429 TooManyRequests; queue invocations if async; emit throttle metric; trigger emergency scaling if account limit permits
|
| 215 |
+
|
| 216 |
+
Condition: ColdStartSpike
|
| 217 |
+
When more than 10 cold starts occur within 5 seconds for a single function
|
| 218 |
+
Then activate predictive pre-warming with increased pool size; check if traffic pattern matches known spike; emit cold-start-spike metric; increase warm pool target by 50%
|
| 219 |
+
|
| 220 |
+
Condition: FunctionErrorRateHigh
|
| 221 |
+
When function error rate exceeds 10% over a 5-minute rolling window
|
| 222 |
+
Then emit error-rate alert; if exceeding 50%, circuit break and return error immediately; send notification to function owner; capture error details in DLQ for analysis
|
| 223 |
+
|
| 224 |
+
Condition: WorkerPoolExhausted
|
| 225 |
+
When no available workers in the execution pool for new invocations
|
| 226 |
+
Then queue invocations with admission control; provision new workers urgently; if provision timeout, fail fast with 503; emit worker-pool-exhausted metric
|
| 227 |
+
|
| 228 |
+
Condition: DLQThresholdReached
|
| 229 |
+
When dead-letter queue depth exceeds 10000 messages per function
|
| 230 |
+
Then pause event trigger to prevent further failures; alert function owner with error summary; enable manual replay mechanism; emit DLQ-critical metric
|
| 231 |
+
|
| 232 |
+
# Level 5: Events
|
| 233 |
+
Event: OnFunctionDeployed
|
| 234 |
+
On new function version deployed to the platform
|
| 235 |
+
Action: Invalidate warm pool for previous version, begin pre-warming new version, update routing to canary or all-at-once, validate health check invocation succeeds, emit deployment metric
|
| 236 |
+
|
| 237 |
+
Event: OnColdStartExecuted
|
| 238 |
+
On function invocation requires new execution environment
|
| 239 |
+
Action: Record cold start duration metric, update warm pool sizing algorithm, check if pre-warming should have prevented, emit cold-start metric with runtime and memory configuration
|
| 240 |
+
|
| 241 |
+
Event: OnInvocationCompleted
|
| 242 |
+
On function execution finishes (success or failure)
|
| 243 |
+
Action: Record execution metrics, persist billing record, return environment to warm pool if healthy, update concurrency counters, emit completion metric with duration and status
|
| 244 |
+
|
| 245 |
+
Event: OnScaleDecision
|
| 246 |
+
On scaling controller adjusts concurrency for a function
|
| 247 |
+
Action: Provision or decommission execution environments, update concurrency state, log scaling reason and metrics, emit scale-decision metric, enforce cooldown periods
|
| 248 |
+
|
| 249 |
+
Event: OnDLQMessageAdded
|
| 250 |
+
On failed invocation message added to dead-letter queue
|
| 251 |
+
Action: Increment DLQ depth counter, emit DLQ metric, notify function owner if threshold reached, preserve full invocation context for replay, schedule DLQ retention cleanup
|
| 252 |
+
|
| 253 |
+
# Level 6: Concurrency
|
| 254 |
+
Parallel:
|
| 255 |
+
Independent function invocations across worker nodes
|
| 256 |
+
Event trigger processing from multiple sources
|
| 257 |
+
Warm pool maintenance and pre-warming
|
| 258 |
+
Billing record persistence and aggregation
|
| 259 |
+
Concurrency scaling decisions per function
|
| 260 |
+
|
| 261 |
+
# Level 7: Optimization
|
| 262 |
+
Optimize: Cold start latency
|
| 263 |
+
Priority: Snapshot-based environment restoration; pre-warmed pool with predictive sizing; lightweight microVM (Firecracker) for fast boot; runtime-specific initialization optimizations; lazy loading of function code
|
| 264 |
+
|
| 265 |
+
Optimize: Invocation throughput
|
| 266 |
+
Priority: Connection pooling for downstream services; keep-alive between invocations; batch event processing for stream triggers; efficient serialization (MessagePack over JSON); lock-free concurrency tracking
|
| 267 |
+
|
| 268 |
+
Optimize: Cost efficiency
|
| 269 |
+
Priority: Granular billing with 1ms resolution; automatic scale-to-zero; memory right-sizing recommendations; spot worker nodes for non-critical functions; tiered pricing for committed usage
|
| 270 |
+
|
| 271 |
+
# Level 8: Learning
|
| 272 |
+
Learn: Optimal warm pool size per function
|
| 273 |
+
Goal: Minimize cold starts while minimizing idle resource cost
|
| 274 |
+
Adapt: warmPoolTargetSize per function
|
| 275 |
+
Based: Historical invocation patterns (time-of-day, day-of-week), cold start frequency, and traffic prediction models over 14-day windows
|
| 276 |
+
|
| 277 |
+
Learn: Optimal memory allocation per function
|
| 278 |
+
Goal: Right-size memory allocation to minimize cost while meeting performance targets
|
| 279 |
+
Adapt: recommendedMemorySize per function
|
| 280 |
+
Based: Actual memory usage patterns, execution duration vs. memory correlation, and cost-performance trade-off analysis
|
| 281 |
+
|
| 282 |
+
Learn: Predictive scaling triggers
|
| 283 |
+
Goal: Pre-scale before anticipated traffic bursts to eliminate cold starts
|
| 284 |
+
Adapt: scalingTriggerThreshold and preWarmDuration per function
|
| 285 |
+
Based: Recurring traffic patterns, event trigger schedules, and known traffic surge events from historical data
|
| 286 |
+
|
| 287 |
+
# Level 9: Security
|
| 288 |
+
Security:
|
| 289 |
+
Encrypt: Function code at rest using AES-256 with per-tenant keys
|
| 290 |
+
Encrypt: Invocation payloads in transit using TLS 1.3
|
| 291 |
+
Encrypt: Environment variables and secrets using envelope encryption with KMS
|
| 292 |
+
Protect: Function isolation via microVM boundaries with minimal attack surface
|
| 293 |
+
Protect: Network access via per-function security groups and VPC configuration
|
| 294 |
+
Protect: Against credential leakage via IAM roles with least-privilege execution roles
|
| 295 |
+
Protect: Billing data integrity via append-only immutable log with cryptographic verification
|
| 296 |
+
|
| 297 |
+
# Level 10: Native
|
| 298 |
+
Native: Rust
|
| 299 |
+
{
|
| 300 |
+
use std::sync::Arc;
|
| 301 |
+
use tokio::sync::{Semaphore, RwLock};
|
| 302 |
+
use std::time::{Duration, Instant};
|
| 303 |
+
|
| 304 |
+
struct FunctionRuntime {
|
| 305 |
+
pool: Arc<WarmPool>,
|
| 306 |
+
concurrency_limiter: Arc<Semaphore>,
|
| 307 |
+
billing: Arc<BillingRecorder>,
|
| 308 |
+
metrics: Arc<MetricsCollector>,
|
| 309 |
+
config: FunctionConfig,
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
struct WarmPool {
|
| 313 |
+
environments: RwLock<Vec<ExecutionEnv>>,
|
| 314 |
+
function_id: String,
|
| 315 |
+
target_size: usize,
|
| 316 |
+
max_size: usize,
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
struct ExecutionEnv {
|
| 320 |
+
id: String,
|
| 321 |
+
container_id: String,
|
| 322 |
+
state: EnvState,
|
| 323 |
+
created_at: Instant,
|
| 324 |
+
last_used: Instant,
|
| 325 |
+
invocation_count: u64,
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
#[derive(PartialEq)]
|
| 329 |
+
enum EnvState {
|
| 330 |
+
Idle,
|
| 331 |
+
InUse,
|
| 332 |
+
PreWarming,
|
| 333 |
+
Draining,
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
impl FunctionRuntime {
|
| 337 |
+
async fn invoke(&self, payload: &[u8]) -> Result<InvocationResult, InvokeError> {
|
| 338 |
+
let start = Instant::now();
|
| 339 |
+
|
| 340 |
+
// Acquire concurrency permit
|
| 341 |
+
let permit = self.concurrency_limiter.try_acquire()
|
| 342 |
+
.map_err(|_| InvokeError::Throttled)?;
|
| 343 |
+
|
| 344 |
+
// Get execution environment
|
| 345 |
+
let (env, cold_start) = match self.pool.acquire().await {
|
| 346 |
+
Some(env) => (env, false),
|
| 347 |
+
None => {
|
| 348 |
+
let env = self.pool.provision_new().await?;
|
| 349 |
+
(env, true)
|
| 350 |
+
}
|
| 351 |
+
};
|
| 352 |
+
|
| 353 |
+
if cold_start {
|
| 354 |
+
self.metrics.record_cold_start(&self.config.function_id);
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
// Execute with timeout enforcement
|
| 358 |
+
let timeout = Duration::from_secs(self.config.timeout as u64);
|
| 359 |
+
let result = tokio::time::timeout(timeout, self.execute(&env, payload)).await;
|
| 360 |
+
|
| 361 |
+
let duration = start.elapsed();
|
| 362 |
+
let billed_ms = (duration.as_millis() as u64).max(1);
|
| 363 |
+
|
| 364 |
+
// Record billing
|
| 365 |
+
self.billing.record(BillingRecord {
|
| 366 |
+
function_id: self.config.function_id.clone(),
|
| 367 |
+
duration_ms: duration.as_millis() as u64,
|
| 368 |
+
billed_ms,
|
| 369 |
+
memory_mb: self.config.memory_size,
|
| 370 |
+
compute_charge: self.calculate_charge(billed_ms),
|
| 371 |
+
timestamp: chrono::Utc::now(),
|
| 372 |
+
}).await;
|
| 373 |
+
|
| 374 |
+
// Return env to pool
|
| 375 |
+
self.pool.release(env).await;
|
| 376 |
+
drop(permit);
|
| 377 |
+
|
| 378 |
+
match result {
|
| 379 |
+
Ok(Ok(response)) => Ok(InvocationResult {
|
| 380 |
+
response,
|
| 381 |
+
duration: duration.as_secs_f64(),
|
| 382 |
+
cold_start,
|
| 383 |
+
}),
|
| 384 |
+
Ok(Err(e)) => Err(e),
|
| 385 |
+
Err(_) => Err(InvokeError::Timeout),
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
fn calculate_charge(&self, billed_ms: u64) -> f64 {
|
| 390 |
+
let gb_seconds = (self.config.memory_size as f64 / 1024.0)
|
| 391 |
+
* (billed_ms as f64 / 1000.0);
|
| 392 |
+
gb_seconds * 0.0000166667 // per GB-second rate
|
| 393 |
+
}
|
| 394 |
+
}
|