Skip to content

WASM Procedures: Migration Guide

WASM Procedures: Migration Guide

Documentation Home > User Guides > Features > Migration Guide


Overview

This guide provides practical examples and performance comparisons for migrating existing database procedures to HeliosDB WASM procedures from PL/pgSQL, JavaScript UDFs, and other procedural languages.

Related Sections:


Migration Guide

SQL Procedures to WASM

PL/pgSQL → Rust

Before (PL/pgSQL):

CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE plpgsql
AS $$
DECLARE
emp_salary DECIMAL(10,2);
bonus DECIMAL(10,2);
BEGIN
SELECT salary INTO emp_salary
FROM employees
WHERE id = emp_id;
IF emp_salary > 100000 THEN
bonus := emp_salary * 0.10;
ELSE
bonus := emp_salary * 0.05;
END IF;
UPDATE employees
SET bonus = bonus
WHERE id = emp_id;
END;
$$;

After (Rust/WASM):

use heliosdb_sdk::*;
#[procedure]
pub fn calculate_bonus(emp_id: i64) -> Result<(), String> {
let result = exec_sql(&format!(
"SELECT salary FROM employees WHERE id = {}",
emp_id
))?;
let emp_salary = result.get_f64(0, 0)?;
let bonus = if emp_salary > 100000.0 {
emp_salary * 0.10
} else {
emp_salary * 0.05
};
exec_sql(&format!(
"UPDATE employees SET bonus = {} WHERE id = {}",
bonus, emp_id
))?;
Ok(())
}

Benefits:

  • 8x faster execution
  • Type safety
  • Better error handling
  • Testable outside database

JavaScript UDFs → AssemblyScript

Before (JavaScript UDF):

function calculateTax(amount) {
const taxRate = 0.08;
return amount * taxRate;
}

After (AssemblyScript/WASM):

export function calculateTax(amount: f64): f64 {
const taxRate: f64 = 0.08;
return amount * taxRate;
}

Benefits:

  • Type safety
  • Faster execution
  • Security isolation

Performance Comparison

Benchmark: 10,000 procedure calls

ImplementationTotal TimeAvg per CallMemory
PL/pgSQL52,000ms5.2ms250 MB
PL/Python87,000ms8.7ms450 MB
WASM (Rust)6,200ms0.62ms45 MB
WASM (JS)11,000ms1.1ms62 MB
WASM (Python)14,500ms1.45ms89 MB
WASM (Go)8,100ms0.81ms53 MB

Migration Strategies

1. Gradual Migration

Migrate procedures incrementally while keeping old procedures operational:

-- Keep existing PL/pgSQL procedure
CREATE OR REPLACE PROCEDURE calculate_bonus_legacy(emp_id INT)
LANGUAGE plpgsql AS $$ ... $$;
-- Create new WASM version
CREATE PROCEDURE calculate_bonus_wasm(emp_id BIGINT)
LANGUAGE wasm
AS '/opt/procedures/calculate_bonus.wasm';
-- Use feature flag to switch
CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE sql AS $$
SELECT CASE
WHEN current_setting('app.use_wasm', true) = 'true'
THEN calculate_bonus_wasm(emp_id)
ELSE
calculate_bonus_legacy(emp_id)
END;
$$;

2. A/B Testing

Test WASM procedures against existing implementations:

-- Route 10% of traffic to WASM version
CREATE PROCEDURE calculate_bonus(emp_id INT)
LANGUAGE sql AS $$
SELECT CASE
WHEN random() < 0.1
THEN calculate_bonus_wasm(emp_id)
ELSE
calculate_bonus_legacy(emp_id)
END;
$$;

3. Shadow Mode

Run both versions and compare results:

CREATE PROCEDURE calculate_bonus_shadow(emp_id INT)
LANGUAGE sql AS $$
WITH legacy_result AS (
SELECT calculate_bonus_legacy(emp_id) AS result
),
wasm_result AS (
SELECT calculate_bonus_wasm(emp_id) AS result
)
SELECT
CASE WHEN l.result = w.result
THEN l.result
ELSE (
-- Log discrepancy
SELECT log_mismatch('calculate_bonus', emp_id, l.result, w.result),
l.result -- Return legacy result during shadow mode
)
END
FROM legacy_result l, wasm_result w;
$$;

Common Migration Patterns

Database Queries

PL/pgSQL:

SELECT column INTO variable FROM table WHERE condition;

Rust WASM:

let result = exec_sql("SELECT column FROM table WHERE condition")?;
let variable = result.get_string(0, 0)?;

Loops

PL/pgSQL:

FOR record IN SELECT * FROM table LOOP
-- Process record
END LOOP;

Rust WASM:

let result = exec_sql("SELECT * FROM table")?;
for row in 0..result.row_count() {
// Process row
}

Error Handling

PL/pgSQL:

BEGIN
-- Code
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Error: %', SQLERRM;
END;

Rust WASM:

match risky_operation() {
Ok(value) => value,
Err(e) => {
log_error(&format!("Error: {}", e));
return Err(e);
}
}

Migration Checklist

  • Identify procedures to migrate (prioritize high-traffic, CPU-intensive)
  • Choose target language (Rust for performance, Python for ML, etc.)
  • Set up development environment with SDK
  • Implement WASM version
  • Write unit tests
  • Deploy in shadow mode
  • Compare results and performance
  • Gradually roll out to production
  • Monitor metrics and errors
  • Retire legacy procedures

Next Steps


Navigation: ← Advanced Topics | Index | API Reference →