
Cloud
Learning Level
Advanced Cloud Functions patterns enable building robust, scalable serverless applications with sophisticated patterns for retries, idempotency, and distributed transactions.
By the end of this lesson, you'll understand:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// Initialize outside function for reuse
admin.initializeApp();
const db = admin.firestore();
// Global variables persist across invocations
const cache = new Map();
const connPool = null;
exports.optimizedFunction = functions
.runWith({
memory: '1GB',
timeoutSeconds: 540,
minInstances: 5 // Reduce cold starts
})
.https.onRequest(async (req, res) => {
// Reuses initialized clients from previous invocation
const data = await db.collection('items').get();
res.json({count: data.size});
});exports.idempotentOperation = functions.firestore
.document('orders/{orderId}')
.onCreate(async (snap, context) => {
const order = snap.data();
const orderId = context.params.orderId;
// Generate idempotency key
const idempotencyKey = `process_order_${orderId}`;
const lockRef = admin.firestore()
.collection('locks')
.doc(idempotencyKey);
try {
// Acquire lock atomically
await lockRef.set({
acquired: true,
timestamp: admin.firestore.FieldValue.serverTimestamp()
}, {merge: true});
// Process only if lock acquired
const lockDoc = await lockRef.get();
if (lockDoc.data().acquired && lockDoc.data().processedBy === admin.auth().currentUser.uid) {
await processOrder(order);
await lockRef.update({processed: true});
}
} catch (error) {
console.error('Idempotency check failed:', error);
// Function will retry, but won't re-process
}
});class RetryManager {
async executeWithRetry(fn, options = {}) {
const {
maxRetries = 3,
initialDelay = 1000,
maxDelay = 32000,
backoffMultiplier = 2,
retryableErrors = ['DEADLINE_EXCEEDED', 'INTERNAL']
} = options;
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
const isRetryable = retryableErrors.includes(error.code);
if (attempt === maxRetries || !isRetryable) {
throw error;
}
// Exponential backoff with jitter
const delay = Math.min(
initialDelay * Math.pow(backoffMultiplier, attempt),
maxDelay
) + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
}
// Usage
const retryManager = new RetryManager();
exports.resilientFunction = functions.pubsub
.topic('my-topic')
.onPublish(async (message) => {
await retryManager.executeWithRetry(
() => callExternalAPI(message.data),
{maxRetries: 3}
);
});exports.distributedTransaction = functions.https.onRequest(async (req, res) => {
const {fromUserId, toUserId, amount} = req.body;
const result = await admin.firestore().runTransaction(async (transaction) => {
const fromRef = admin.firestore().collection('users').doc(fromUserId);
const toRef = admin.firestore().collection('users').doc(toUserId);
const fromSnap = await transaction.get(fromRef);
const toSnap = await transaction.get(toRef);
if (!fromSnap.exists || !toSnap.exists) {
throw new Error('User not found');
}
const fromBalance = fromSnap.data().balance;
if (fromBalance < amount) {
throw new Error('Insufficient balance');
}
// Update balances
transaction.update(fromRef, {
balance: fromBalance - amount
});
transaction.update(toRef, {
balance: (toSnap.data().balance || 0) + amount
});
// Create transaction record
const txnRef = admin.firestore().collection('transactions').doc();
transaction.set(txnRef, {
from: fromUserId,
to: toUserId,
amount,
timestamp: admin.firestore.FieldValue.serverTimestamp()
});
return {success: true, transactionId: txnRef.id};
});
res.json(result);
});// Base function with middleware pattern
const createSecureFunction = (handlerFn) => {
return functions.https.onRequest(async (req, res) => {
// Authentication middleware
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).send('Unauthorized');
try {
const decodedToken = await admin.auth().verifyIdToken(token);
req.userId = decodedToken.uid;
} catch (error) {
return res.status(403).send('Invalid token');
}
// Validation middleware
if (!req.body || !req.body.data) {
return res.status(400).send('Missing data');
}
// Call handler
try {
const result = await handlerFn(req, res);
res.json(result);
} catch (error) {
res.status(500).json({error: error.message});
}
});
};
// Usage
exports.mySecureFunction = createSecureFunction(async (req, res) => {
// Your business logic here
return {success: true};
});class FunctionProfiler {
startProfile(functionName) {
return {
functionName,
startTime: Date.now(),
startMemory: process.memoryUsage().heapUsed,
end: function() {
const duration = Date.now() - this.startTime;
const memory = process.memoryUsage().heapUsed - this.startMemory;
console.log(JSON.stringify({
function: this.functionName,
duration_ms: duration,
memory_delta_mb: (memory / 1024 / 1024).toFixed(2),
timestamp: new Date().toISOString()
}));
return {duration, memory};
}
};
}
}
// Usage
const profiler = new FunctionProfiler();
exports.profiledFunction = functions.https.onRequest(async (req, res) => {
const profile = profiler.startProfile('profiledFunction');
try {
// Function logic
await doWork();
} finally {
profile.end();
}
});Learn about function orchestration with Workflows, or explore integration patterns with GCP services.
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward