Buffering & Flushing
Smart strategies for buffering and flushing logs in Triton for optimal performance and reliability across Apex and LWC.
Understanding Buffering vs Flushing
Apex:
addLog(builder)
buffers.log(builder)
flushes immediately (use at key boundaries or errors).Triton sets stack trace & operation automatically when missing, and appends governor limit info via the builder.
LWC:
triton.log(builder)
buffers in memory.triton.logNow(builder)
flushes that one immediately.Triton's LWC TransactionManager auto-flushes after idle periods; still flush on navigation/unload when practical.
Real-World Example: Financial Transaction Processing
public class PaymentProcessor {
public static void processPayment(Id accountId, Decimal amount) {
Long startTime = System.now().getTime();
// Set up template for all payment processing logs
Triton.instance.setTemplate(
Triton.makeBuilder()
.category(TritonTypes.Category.Integration)
.type(TritonTypes.Type.Backend)
.area(TritonTypes.Area.PaymentProcessing)
.relatedObject(accountId)
);
// Buffer routine operations using template
Triton.instance.addLog(
Triton.instance.fromTemplate()
.level(TritonTypes.Level.DEBUG)
.summary('Payment processing started')
.details('accountId=' + accountId + ', amount=' + amount)
);
try {
// First, make the callout to payment gateway
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.paymentgateway.com/process');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(new Map<String, Object>{
'accountId' => accountId,
'amount' => amount,
'currency' => 'USD'
}));
HttpResponse response = new Http().send(req);
if (response.getStatusCode() == 200) {
// Parse successful response
Map<String, Object> responseData = (Map<String, Object>)JSON.deserializeUntyped(response.getBody());
// Now perform DML after successful callout
Payment__c payment = new Payment__c(
Account__c = accountId,
Amount__c = amount,
Status__c = 'Completed'
);
insert payment;
// Buffer success milestone using template
Triton.instance.addLog(
Triton.instance.fromTemplate()
.level(TritonTypes.Level.INFO)
.summary('Payment processed successfully')
.details('paymentId=' + payment.Id)
.relatedObject(payment.Id)
.integrationPayload(req, response)
.duration(System.now().getTime() - startTime)
);
} else {
// Immediately flush critical error using template
Triton.instance.log(
Triton.instance.fromTemplate()
.exception(new PaymentException('Gateway error: ' + response.getStatusCode()))
.summary('Payment gateway failed')
.integrationPayload(req, response)
);
}
} catch (Exception e) {
// Immediately flush critical errors using template
Triton.instance.log(
Triton.instance.fromTemplate()
.exception(e)
.summary('Payment processing failed')
);
throw e;
} finally {
// Flush all buffered logs at transaction boundary
Triton.instance.flush();
}
}
}
LWC Buffering Example
// E-commerce checkout component with smart buffering
export default class CheckoutComponent extends LightningElement {
triton;
connectedCallback() {
this.triton = new Triton().bindToComponent('c-checkout');
// Set up template for all checkout operations
this.triton.setTemplate(
this.triton.makeBuilder()
.type(TYPE.FRONTEND)
.area(AREA.COMMUNITY)
);
}
async handleCheckout() {
// Buffer routine debug info
this.triton.log(
this.triton.fromTemplate()
.level(LEVEL.DEBUG)
.summary('Checkout initiated')
.details(JSON.stringify({
cartItems: this.cartItems.length,
totalAmount: this.totalAmount
}))
);
try {
// Process checkout steps
await this.validateInventory();
await this.processPayment();
await this.createOrder();
// Buffer success milestone
this.triton.log(
this.triton.fromTemplate()
.level(LEVEL.INFO)
.summary('Checkout completed successfully')
.details(JSON.stringify({ orderId: this.orderId }))
);
} catch (error) {
// Immediately flush critical errors
await this.triton.logNow(
this.triton.fromTemplate()
.exception(error)
.summary('Checkout failed')
);
// Show user-friendly error
this.showErrorMessage('Checkout failed. Please try again or contact support.');
}
}
disconnectedCallback() {
// Flush any remaining logs when component is destroyed
this.triton.flush();
}
}
When to Buffer vs Flush
Buffer When:
Routine operations: Normal processing steps, debug information
High-volume scenarios: Processing multiple records in loops
Performance-critical paths: Where logging overhead must be minimized
Non-critical information: Debug logs, informational milestones
Flush Immediately When:
Critical errors: Exceptions that might prevent further processing
Transaction boundaries: End of methods, batch chunks, queueable completion
User-facing failures: Errors that affect user experience
Integration failures: Callout errors, external service issues
Component teardown: LWC component destruction
Performance Considerations
Apex Performance
Buffering reduces DML: Multiple logs are sent together, reducing platform event overhead
Flush strategically: Use
flush()
at natural boundaries to avoid memory buildupError scenarios: Always flush on errors to ensure critical information is captured
Governor limits: Monitor platform event limits in high-volume scenarios
LWC Performance
Memory management: Buffered logs consume memory until flushed
Auto-flush: Triton automatically flushes after idle periods
Navigation events: Flush on page navigation or component destruction
Error handling: Use
logNow()
for critical errors to ensure immediate capture
Best Practices
Buffer Management
Set clear boundaries: Define when to flush based on logical transaction boundaries
Monitor memory usage: Be aware of buffered log volume in long-running processes
Error handling: Always flush on exceptions to preserve critical context
Performance testing: Measure the impact of buffering vs immediate flushing
Flush Strategies
Method boundaries: Flush at the end of public methods
Batch operations: Flush after each batch chunk
Queueable completion: Flush at the end of queueable execution
Component lifecycle: Flush in LWC
disconnectedCallback()
Error scenarios: Immediate flush for all exceptions
Monitoring and Debugging
Log volume: Monitor the number of buffered vs flushed logs
Memory usage: Track memory consumption in long-running processes
Error correlation: Ensure errors are properly flushed for debugging
Performance impact: Measure the overhead of different buffering strategies
Advanced Patterns
Conditional Flushing
public void processRecords(List<SObject> records) {
for (SObject record : records) {
// Buffer routine processing
Triton.instance.addLog(
Triton.instance.fromTemplate()
.summary('Processing record')
.relatedObject(record.Id)
);
try {
processRecord(record);
} catch (Exception e) {
// Flush immediately on error
Triton.instance.log(
Triton.instance.fromTemplate()
.exception(e)
.summary('Record processing failed')
);
throw e;
}
}
// Flush all buffered logs at the end
Triton.instance.flush();
}
Smart Buffering with Limits
public class SmartBuffer {
private static final Integer MAX_BUFFER_SIZE = 50;
private static Integer bufferCount = 0;
public static void addLog(TritonBuilder builder) {
Triton.instance.addLog(builder);
bufferCount++;
// Flush when buffer gets too large
if (bufferCount >= MAX_BUFFER_SIZE) {
Triton.instance.flush();
bufferCount = 0;
}
}
public static void forceFlush() {
Triton.instance.flush();
bufferCount = 0;
}
}
By mastering buffering and flushing strategies, you'll optimize performance while ensuring critical logs are never lost and debugging information is always available when needed.
Last updated