Transaction Management

Correlating logs across LWC → Apex → Flow boundaries using transaction management for end-to-end traceability.

Transaction Handling & Correlation

  • Start early (user entry points): Triton.instance.startTransaction() (Apex) or triton.startTransaction() (LWC).

  • Resume across hops:

    • Capture the current transactionId in LWC (triton.transactionId) and pass it in Apex method params.

    • In Apex entry, call Triton.instance.resumeTransaction(txId) if provided; otherwise withCache() can auto-resume if configured.

  • Stop & flush at natural boundaries: end of a long-running flow, batch finish, queueable completion, on modal close/navigation in LWC.

Apex (Controller Entry)

@AuraEnabled(cacheable=false)
public static List<Opportunity> loadOpps(Id accountId, String txId) {
    if (String.isNotBlank(txId)) Triton.instance.resumeTransaction(txId);
    else Triton.instance.startTransaction();

    // Set up template for opportunity loading
    Triton.instance.setTemplate(
        Triton.makeBuilder()
            .category(TritonTypes.Category.Apex)
            .type(TritonTypes.Type.Backend)
            .area(TritonTypes.Area.OpportunityManagement)
            .relatedObject(accountId)
    );

    try {
        // ... do work
        Triton.instance.log(
            Triton.instance.fromTemplate()
                .level(TritonTypes.Level.INFO)
                .summary('Loaded opportunities')
                .details('accountId=' + accountId)
        );
        return [SELECT Id, Name, Amount FROM Opportunity WHERE AccountId = :accountId LIMIT 200];
    } catch (Exception e) {
        Triton.instance.log(
            Triton.instance.fromTemplate()
                .exception(e)
        );
        throw e;
    } finally {
        Triton.instance.stopTransaction();
    }
}

LWC (Passing Transaction Across)

import loadOpps from '@salesforce/apex/OppController.loadOpps';
const triton = new Triton().bindToComponent('c-opportunity-list');
const txId = triton.startTransaction();

// Set up template for opportunity operations
triton.setTemplate(
    triton.makeBuilder()
        .type(TYPE.FRONTEND)
        .area(AREA.OPPORTUNITY_MANAGEMENT)
        .relatedObjects([this.recordId])
);

try {
  const data = await loadOpps({ accountId: this.recordId, txId });
  triton.log(
    triton.fromTemplate()
      .summary('Opps rendered')
      .details(`rows=${data.length}`)
  );
} catch (error) {
  triton.logNow(
    triton.fromTemplate()
      .exception(error)
      .summary('Failed to load opps')
  );
} finally {
  triton.stopTransaction();
}

Real-World Example: Multi-Step Lead Conversion Process

// Lead conversion LWC with transaction correlation
export default class LeadConversionWizard extends LightningElement {
    triton;
    
    connectedCallback() {
        this.triton = new Triton().bindToComponent('c-lead-conversion-wizard');
        this.triton.startTransaction();
        
        // Set up template for lead conversion operations
        this.triton.setTemplate(
            this.triton.makeBuilder()
                .type(TYPE.FRONTEND)
                .area(AREA.LEAD_CONVERSION)
                .relatedObjects([this.recordId])
        );
    }
    
    async handleLeadConversion() {
        try {
            // Step 1: Validate lead data
            await this.validateLeadData();
            
            // Step 2: Create account and contact
            const accountResult = await createAccount({ 
                accountData: this.accountData,
                txId: this.triton.transactionId 
            });
            
            // Step 3: Create opportunity
            const oppResult = await createOpportunity({ 
                oppData: this.oppData,
                accountId: accountResult.Id,
                txId: this.triton.transactionId 
            });
            
            // Step 4: Update lead status
            await updateLeadStatus({ 
                leadId: this.recordId,
                status: 'Converted',
                txId: this.triton.transactionId 
            });
            
            this.triton.log(
                this.triton.fromTemplate()
                    .summary('Lead conversion completed')
                    .details(JSON.stringify({
                        accountId: accountResult.Id,
                        opportunityId: oppResult.Id,
                        leadId: this.recordId
                    }))
                    .relatedObjects([accountResult.Id, oppResult.Id])
            );
            
        } catch (error) {
            await this.triton.logNow(
                this.triton.fromTemplate()
                    .exception(error)
                    .summary('Lead conversion failed')
            );
        } finally {
            this.triton.stopTransaction();
        }
    }
}
// Apex controller with transaction resumption
public class LeadConversionController {
    @AuraEnabled
    public static Account createAccount(AccountData accountData, String txId) {
        // Resume transaction from LWC
        if (String.isNotBlank(txId)) {
            Triton.instance.resumeTransaction(txId);
        } else {
            Triton.instance.startTransaction();
        }
        
        // Set up template for lead conversion operations
        Triton.instance.setTemplate(
            Triton.makeBuilder()
                .category(TritonTypes.Category.Apex)
                .type(TritonTypes.Type.Backend)
                .area(TritonTypes.Area.LeadConversion)
                .relatedObject(accountData.leadId)
        );
        
        try {
            Account newAccount = new Account(
                Name = accountData.name,
                Industry = accountData.industry,
                BillingCity = accountData.city
            );
            insert newAccount;
            
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .level(TritonTypes.Level.INFO)
                    .summary('Account created for lead conversion')
                    .details('accountId=' + newAccount.Id + ', name=' + accountData.name)
                    .relatedObject(newAccount.Id)
            );
            
            return newAccount;
            
        } catch (Exception e) {
            Triton.instance.log(
                Triton.instance.fromTemplate()
                    .exception(e)
                    .summary('Account creation failed')
            );
            throw e;
        }
    }
}

Transaction ID Flow

Understanding how the transaction ID connects logs across contexts is crucial:

  1. LWC Initialization

    • Direct Params: Transaction ID is generated when first log is created

    • Cache: Transaction ID is generated and automatically cached

  2. Apex Execution

    • Direct Params: Transaction ID is passed via method parameter and resumed

    • Cache: Transaction ID is automatically retrieved from cache

  3. Flow Execution

    • The Flow's Interview GUID is automatically associated with the transaction

    • All Flow logs are grouped under the same transaction

Best Practices

Transaction Lifecycle

  1. Start early: Begin transactions at user entry points

  2. Resume consistently: Always resume transactions in Apex when txId is provided

  3. Stop properly: End transactions at natural boundaries

  4. Handle failures: Ensure transactions are stopped even when errors occur

Correlation Strategies

  1. Direct parameter passing: Explicitly pass transaction IDs for simple scenarios

  2. Platform cache: Use cache for complex, multi-component scenarios

  3. Interview GUID: Always use Flow Interview GUID for Flow correlation

  4. Error handling: Maintain transaction context even during failures

Performance Considerations

  1. Cache availability: Check if platform cache is enabled before using cache-based correlation

  2. Memory management: Be aware of transaction context memory usage in long-running processes

  3. Boundary management: Stop transactions promptly to free resources

By mastering transaction management, you'll create complete execution traces that span across all Salesforce technologies, enabling powerful debugging and monitoring capabilities.

Last updated