OCI Functions Advanced: Concurrency, Cold Start Optimization, and Event Triggers

OCI Functions runs your code without any server management, scaling from zero to thousands of concurrent executions automatically. The practical challenges are cold start latency for latency-sensitive workloads, concurrency control to prevent runaway execution, retry patterns for unreliable event sources, and observability when functions are invisible in your monitoring. This post covers provisioned concurrency, reserved concurrency, APM tracing, and dead letter queue patterns with Terraform.

Step 1: Application and Function with IAM

resource "oci_identity_dynamic_group" "functions_runners" {
  compartment_id = var.tenancy_ocid
  name           = "functions-dynamic-group"
  description    = "OCI Functions execution principals"
  matching_rule  = "All {resource.type = 'fnfunc', resource.compartment.id = 'COMPARTMENT_OCID'}"
}

resource "oci_identity_policy" "functions_policy" {
  compartment_id = var.compartment_id
  name           = "functions-execution-policy"
  statements = [
    "Allow dynamic-group functions-dynamic-group to read secret-bundle in compartment id COMPARTMENT_OCID",
    "Allow dynamic-group functions-dynamic-group to manage objects in compartment id COMPARTMENT_OCID",
    "Allow dynamic-group functions-dynamic-group to use ons-topics in compartment id COMPARTMENT_OCID",
    "Allow dynamic-group functions-dynamic-group to manage queues in compartment id COMPARTMENT_OCID"
  ]
}

resource "oci_functions_application" "orders_app" {
  compartment_id = var.compartment_id
  display_name   = "orders-processing-app"
  subnet_ids     = [var.functions_subnet_id]
  shape          = "GENERIC_X86"

  config = {
    DB_HOST         = var.db_host
    REGION          = var.region
    LOG_LEVEL       = "INFO"
    QUEUE_ENDPOINT  = var.queue_endpoint
  }

  trace_config {
    is_enabled = true
    domain_id  = var.apm_domain_id
  }

  defined_tags = { "Operations.Environment" = "production", "Operations.ManagedBy" = "terraform" }
}

resource "oci_functions_function" "process_order" {
  application_id = oci_functions_application.orders_app.id
  display_name   = "process-order"
  image          = "me-jeddah-1.ocir.io/NAMESPACE/production/process-order:2.1.0"
  memory_in_mbs  = 512
  timeout_in_seconds = 120

  # Provisioned concurrency - pre-warms instances to eliminate cold start
  provisioned_concurrency_config {
    strategy = "CONSTANT"
    count    = 5  # Keep 5 warm instances ready at all times
  }

  config = {
    MAX_RETRY_COUNT = "3"
    DLQ_QUEUE_ID    = var.orders_dlq_queue_id
  }

  defined_tags = { "Operations.Environment" = "production" }
}

output "function_id" { value = oci_functions_function.process_order.id }

Step 2: OCI Queue as DLQ for Failed Invocations

resource "oci_queue_queue" "orders_processing_queue" {
  compartment_id                    = var.compartment_id
  display_name                      = "orders-processing-queue"
  visibility_in_seconds             = 30
  timeout_in_seconds                = 120
  retention_in_seconds              = 86400
  max_delivery_count                = 3
  dead_letter_queue_delivery_count  = 3
  defined_tags                      = { "Operations.ManagedBy" = "terraform" }
}

resource "oci_queue_queue" "orders_dlq" {
  compartment_id          = var.compartment_id
  display_name            = "orders-processing-dlq"
  visibility_in_seconds   = 30
  timeout_in_seconds      = 600
  retention_in_seconds    = 604800  # 7 days
  max_delivery_count      = 1
}

resource "oci_monitoring_alarm" "dlq_messages_arrived" {
  compartment_id        = var.compartment_id
  display_name          = "orders-dlq-messages-alert"
  is_enabled            = true
  metric_compartment_id = var.compartment_id
  namespace             = "oci_queue"
  query                 = "ApproximateNumberOfMessages[5m]{queueId = 'DLQ_OCID'}.max() > 0"
  severity              = "CRITICAL"
  pending_duration      = "PT5M"
  destinations          = [var.ops_notification_topic_id]
  body                  = "Messages in the orders DLQ. The process-order function has exceeded max retry count. Investigate function errors in APM traces."
}

Step 3: Multi-Trigger Setup

resource "oci_functions_function" "remediate_bucket" {
  application_id     = oci_functions_application.orders_app.id
  display_name       = "remediate-public-bucket"
  image              = "me-jeddah-1.ocir.io/NAMESPACE/tools/remediate-bucket:1.0.2"
  memory_in_mbs      = 256
  timeout_in_seconds = 30

  # No provisioned concurrency needed - event-triggered, latency not critical
  provisioned_concurrency_config {
    strategy = "NONE"
  }
}

# Trigger: OCI Events routes public bucket creation to this function
resource "oci_events_rule" "public_bucket_trigger" {
  compartment_id = var.compartment_id
  display_name   = "trigger-remediate-on-public-bucket"
  is_enabled     = true

  condition = jsonencode({
    "eventType" = ["com.oraclecloud.objectstorage.createbucket"]
    "data"      = { "publicAccessType" = ["ObjectRead", "ObjectReadWithoutList"] }
  })

  actions {
    actions {
      action_type = "FAAS"
      is_enabled  = true
      function_id = oci_functions_function.remediate_bucket.id
    }
  }
}

Operational Notes

Provisioned concurrency eliminates cold starts by keeping a fixed number of function instances initialized and ready. You pay for the reserved instances even when they are not processing invocations. Use it for user-facing functions where cold start adds noticeable latency. Do not use it for background processing or event-triggered functions where cold start is acceptable. Set the count to your p99 concurrent invocation count, not the peak, to avoid over-provisioning.

Dead letter queues catch messages that fail after all retry attempts. Without a DLQ, a message that causes the function to error repeatedly is dropped after max retries. With a DLQ, the failed message is preserved for investigation and reprocessing. Monitor the DLQ depth with an alarm set to trigger on any message arrival, since messages in the DLQ always indicate a processing failure that needs attention.

Regards,
Osama

#OCI #OracleCloud #Functions #Serverless #Terraform #IaC #TechBlog #Oracle #PlatformEngineering #CloudNative #DevOps #OracleCloudInfrastructure #ColdStart #ProvisionedConcurrency #EventDriven #Queue #DLQ #APM #Observability #FaaS

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.