The Challenge
When building a function execution platform, one of the hardest problems is tracking how functions call other functions. Users want to see complete execution trees showing every nested call, input, output, and timing information—without having to manually instrument their code.
How It Works
SINAS automatically tracks function execution using Python's Abstract Syntax Tree (AST). When you upload a function, we parse the code, inject tracking decorators, and maintain the execution tree automatically.
Step 1: AST Parsing
When you create a function, SINAS parses your Python code into an AST:
def calculate_order_total(order_id):
customer = get_customer(order_id)
discount = calculate_discount(customer.tier)
tax = calculate_tax(order.subtotal)
return order.subtotal - discount + tax
Step 2: Automatic Decorator Injection
SINAS automatically adds a @track decorator to every function definition. This decorator captures:
- Function name and version
- Input parameters (serialized with dill)
- Output values
- Start and end timestamps
- Parent execution context
- Errors and tracebacks
Step 3: Execution Tree Building
When a function calls another SINAS function, the tracking system automatically links them in an execution tree. The result is a complete visualization of your function's behavior:
Execution: calculate_order_total
├─ Step: get_customer (completed, 150ms)
├─ Step: calculate_discount (completed, 50ms)
│ └─ Step: apply_promo_code (completed, 30ms)
└─ Step: calculate_tax (completed, 40ms)
Benefits
Zero Instrumentation
Write normal Python code. No special imports, no manual logging, no wrapper functions. Just pure business logic.
Complete Visibility
See exactly what happened during an execution:
- Which functions were called and in what order
- What inputs were passed to each function
- What outputs each function returned
- How long each function took
- Where errors occurred in the call chain
Automatic Versioning
Every time you update function code, SINAS creates a new version. Execution records always reference the exact version that ran, making debugging historical issues trivial.
Performance Considerations
The tracking overhead is minimal:
- AST parsing happens once at upload time
- Execution tracking adds ~1-5ms per function call
- Large inputs/outputs are sampled or truncated
- Tracking can be disabled for performance-critical functions
Use Cases
Debugging Complex Workflows
When a webhook fails at 3 AM, you can trace the exact execution path and see where it went wrong.
Performance Optimization
Identify bottlenecks by analyzing execution times across the call tree.
Auditing and Compliance
Maintain complete records of what code ran, what data it processed, and what results it produced.
Technical Deep Dive
For those interested in the implementation details, the AST transformation uses Python's ast.NodeTransformer to wrap function definitions:
class TrackingInjector(ast.NodeTransformer):
def visit_FunctionDef(self, node):
# Inject @track decorator
track_decorator = ast.Name(id='track', ctx=ast.Load())
node.decorator_list.insert(0, track_decorator)
return node
The @track decorator captures execution context from thread-local storage, allowing nested calls to automatically link to their parent execution.
Conclusion
Automatic execution tracking is one of those features that seems simple on the surface but provides immense value in production. By eliminating manual instrumentation, SINAS lets developers focus on business logic while maintaining complete observability.