Skip to content
/ pay Public

High-performance async payment transaction engine in Rust with comprehensive profiling and benchmarks. Features: 5.6M tx/sec, fixed-point arithmetic, concurrent processing, full dispute workflows. Includes 6 profiling scenarios and extensive documentation.

Notifications You must be signed in to change notification settings

serle/pay

Repository files navigation

Payment Transaction Engine

A high-performance payment transaction engine built in Rust that processes CSV transaction streams, handles disputes and chargebacks, and outputs account states. This implementation demonstrates production-ready architectural patterns including async streaming, concurrent processing, lock-free data structures, and comprehensive testing.

Project Goals:

  • Process transactions from CSV input (deposits, withdrawals, disputes, resolves, chargebacks)
  • Handle thousands of concurrent transaction streams efficiently
  • Maintain financial accuracy using fixed-point arithmetic
  • Provide immediate CLI utility while being embeddable in server applications
  • Demonstrate best practices in Rust: type safety, error handling, testing, and performance optimization

Features

Transaction Processing

  • Deposits: Credit client accounts
  • Withdrawals: Debit client accounts (with insufficient funds protection)
  • Disputes: Hold funds pending investigation
  • Resolves: Release disputed funds back to available
  • Chargebacks: Reverse disputed transactions and freeze accounts

Architecture Highlights

  • Async Streaming: Never loads entire dataset into memory
  • Concurrent-Safe: DashMap enables lock-free concurrent account access
  • Type-Safe: Fixed-point arithmetic prevents floating-point errors
  • Error Resilient: Pluggable error policies (skip invalid, abort on error, silent)
  • Layered Design: Domain → Storage → Engine → Streaming → IO → App
  • Signal Handling: Graceful shutdown on SIGINT/SIGTERM/SIGHUP
  • Future-Proof: Embeddable in server with thousands of concurrent TCP streams

Quick Start

Build

cargo build --release

Run

# Process transactions and output account states
cargo run --release -- transactions.csv > accounts.csv

# Suppress error logging (only show output)
cargo run --release -- transactions.csv 2>/dev/null > accounts.csv

Test

# Run all tests (153 unit + 10 integration passing)
cargo test

# Run with sample fixtures
cargo run -- tests/fixtures/simple.csv
cargo run -- tests/fixtures/disputes.csv
cargo run -- tests/fixtures/errors.csv

Automated Testing

The project includes a comprehensive automated test suite that simulates the actual automated scoring environment:

# Run all 14 automated test scenarios
./auto_tester/run_tests.sh

Test Coverage:

  • ✅ 14 test scenarios covering all brief requirements
  • ✅ Basic deposits/withdrawals, dispute workflows, error handling
  • ✅ Multiple clients, decimal precision, locked accounts
  • ✅ Edge cases: empty CSV, whitespace, large amounts, client mismatches
  • ✅ Row-order-agnostic comparison (per brief specification)
  • ✅ Clear pass/fail reporting with exit codes

Output when all tests pass:

╔════════════════════════════════════════════════════════════╗
║  ALL TESTS PASSED 🎉                                       ║
╚════════════════════════════════════════════════════════════╝

See auto_tester/README.md for detailed documentation.

Input Format

CSV with columns: type, client, tx, amount

type,client,tx,amount
deposit,1,1,1.0
deposit,2,2,2.0
withdrawal,1,3,0.5
dispute,1,1,
resolve,1,1,
chargeback,1,1,

Field Specifications:

  • type: String (deposit, withdrawal, dispute, resolve, chargeback)
  • client: u16 client ID (0-65535)
  • tx: u32 transaction ID (0-4294967295, globally unique)
  • amount: Decimal with up to 4 decimal places (required for deposit/withdrawal only)

Assumptions:

  • Transactions are processed in chronological order (as they appear in file)
  • Client IDs and transaction IDs are not necessarily ordered
  • Whitespace is trimmed automatically
  • Missing clients are created on first transaction

Output Format

CSV with columns: client, available, held, total, locked

client,available,held,total,locked
1,1.5000,0.0000,1.5000,false
2,2.0000,0.0000,2.0000,false

Column Definitions:

  • available: Funds available for trading/withdrawal (total - held)
  • held: Funds held due to disputes (total - available)
  • total: Total account balance (available + held)
  • locked: Account frozen due to chargeback

Guarantees:

  • All amounts displayed with exactly 4 decimal places
  • Row ordering is non-deterministic (as allowed by spec)
  • Invariant: total = available + held (enforced by type system)

Architecture & Design Decisions

1. Fixed-Point Arithmetic

  • Decision: Use i64 multiplied by 10,000 instead of f64
  • Rationale: Eliminates floating-point rounding errors critical for financial calculations
  • Trade-off: Limited to ±922,337,203,685,477.5807 (far exceeding u16 client practical limits)

2. Async Streaming with Backpressure

  • Decision: Use Tokio + futures async streams from the start
  • Rationale: Brief emphasizes "thousands of concurrent TCP streams" and efficiency
  • Benefit: Natural backpressure via Poll::Pending, scales to server use case without redesign
  • Trade-off: More complex than synchronous iterators, but addresses stated requirements

3. DashMap for Concurrent Storage

  • Decision: Use DashMap (lock-free concurrent HashMap) over RwLock/Mutex
  • Rationale: Per-shard locking enables non-blocking snapshots during concurrent updates
  • Benefit: O(1) account lookups with minimal contention
  • Trade-off: Non-deterministic iteration order (acceptable per spec: "Row ordering does not matter")

4. Entry Pattern for Atomic Updates

  • Decision: Lazy write-locking via entry() API
  • Rationale: Prevents TOCTOU (time-of-check-time-of-use) race conditions
  • Guarantee: Account updates are atomic even under concurrent access
  • Example: Get-or-create account + apply operation in single lock acquisition

5. Separate Transaction Variants

  • Decision: enum Transaction { Deposit{amount}, Withdrawal{amount}, Dispute, ... }
  • Rationale: Type system prevents Option<amount> runtime checks
  • Benefit: Compile-time guarantee disputes don't have amounts, deposits do
  • Pattern: Make invalid states unrepresentable

6. Layered Error Handling

  • Decision: Error type per layer using thiserror, with From trait conversions
  • Layers: DomainErrorStorageErrorEngineErrorIoErrorAppError
  • Benefit: Each layer handles its own concerns, error context preserved upward
  • Policy: Pluggable via ErrorPolicy trait (SkipErrors, AbortOnError, SilentSkip)

7. Private Account Fields with Public Getters

  • Decision: All ClientAccount fields private, total() derived from available + held
  • Rationale: Prevents invariant violations (e.g., manually setting total != available + held)
  • Pattern: Smart constructors + derived values eliminate entire bug classes

8. Transaction Store for Dispute Resolution

  • Decision: Store all deposits in HashMap<u32, TransactionRecord> with dispute flag
  • Rationale: Disputes reference transactions by ID, requiring historical lookup
  • Assumption: Only deposits can be disputed (per common banking practice)
  • Future: Could use LRU cache or external DB for billion+ transaction scale

9. Reusable CLI Abstraction

  • Decision: CliApp wrapper handles signals, buffering, exit codes
  • Benefit: Separates infrastructure (Unix signals, stdout flushing) from business logic
  • Features: SIGINT/SIGTERM/SIGHUP handling, explicit flush before exit, proper exit codes
  • Pattern: Generic over application logic via FnOnce() -> Future<Result<R, AppError>>

10. Compatibility Layer for AsyncRead

  • Decision: Use tokio-util::compat to bridge tokio::io ↔ futures::io
  • Rationale: csv-async expects futures::io::AsyncRead, tokio types implement tokio::io::AsyncRead
  • Benefit: Zero-cost abstraction, works with both ecosystems
  • Pattern: Composability via trait adapters

Business Logic & Assumptions

Dispute Semantics

Assumption: Only deposits can be disputed, not withdrawals.

Rationale:

  • Withdrawals represent funds leaving the system (already gone)
  • Disputing a withdrawal doesn't make business sense
  • Common banking practice: disputes apply to incoming funds (deposits)

Implementation: EngineError::CannotDisputeWithdrawal prevents withdrawal disputes

Client Mismatch Protection

Enhancement: Disputes/resolves/chargebacks verify client_id matches transaction.

Rationale:

  • Brief doesn't explicitly require this
  • Prevents client A from disputing client B's transaction
  • Safety feature beyond spec requirements

Implementation: Check account.client_id() == tx_record.client_id

Locked Account Behavior

Requirement: "If a chargeback occurs the client's account should be immediately frozen"

Implementation:

  • All operations (deposits, withdrawals, disputes) blocked on locked accounts
  • Account remains locked permanently (no unlock mechanism)
  • Tested in transactions_on_locked_account integration test

Rationale: Frozen accounts prevent further fraudulent activity

Insufficient Funds

Requirement: "If a client does not have sufficient available funds the withdrawal should fail"

Implementation:

  • Check available >= amount before withdrawal
  • Account state unchanged on failure
  • Error logged but processing continues (per "ignore errors" guidance)

Tested: insufficient_funds_ignored integration test

Invalid Transaction References

Requirement: "If the tx specified doesn't exist... you can ignore it and assume this is an error on our partners side"

Implementation:

  • TransactionNotFound error logged to stderr
  • Processing continues (permissive error policy)
  • Account state unchanged

Applies to: Disputes, resolves, chargebacks referencing non-existent tx IDs

Testing Strategy

Test Coverage: 163 Tests

  • Domain Layer (54 tests): Pure functions, business logic
  • Storage Layer (16 tests): Concurrent access, atomicity
  • Engine Layer (17 tests): Transaction processing, dispute workflows
  • IO Layer (29 tests): CSV parsing, error handling
  • Streaming Layer (16 tests): Error policies, stream processor, topologies
  • App Layer (8 tests): Error unification, CLI abstraction
  • Integration (10 tests): End-to-end scenarios with realistic data

Test Philosophy

  1. Unit tests at layer boundaries: Each module tests its own logic in isolation
  2. Integration tests with fixtures: Realistic CSV data validates end-to-end behavior
  3. Type system over runtime checks: Make invalid states unrepresentable
  4. Concurrent correctness: Multi-threaded tests verify atomicity guarantees

Sample Fixtures

  • simple.csv: Basic deposits and withdrawals
  • disputes.csv: Full dispute lifecycle (dispute → resolve/chargeback)
  • errors.csv: Invalid transactions (insufficient funds, missing tx)

Running Tests

# All tests
cargo test

# Specific layer
cargo test --lib domain::
cargo test --test integration_test

# With output
cargo test -- --nocapture

Performance Benchmarking

The project includes comprehensive performance benchmarks using Criterion.rs to validate architectural claims and detect performance regressions.

Benchmark Categories

  1. Transaction Processing - Single-threaded baseline (deposits, withdrawals, disputes, mixed workloads)
  2. Storage Operations - DashMap performance (account lookups, updates, cold/hot cache)
  3. Concurrent Streams - Scaling from 1 to 10,000 concurrent streams (validates "thousands of concurrent TCP streams" claim)
  4. Stream Topologies - ⭐ NEW: Compares Chain vs Merge, shard scaling (1-8 shards), and assignment strategies
  5. End-to-End - Real-world CSV pipeline with different dataset sizes and transaction patterns
  6. Runtime Comparison - Threading analysis (single-threaded vs multi-threaded Tokio)

Running Benchmarks

# Run all benchmarks (takes 15-20 minutes)
cargo bench

# Run specific benchmark suite
cargo bench --bench transaction_processing  # Core transaction processing
cargo bench --bench storage_operations      # DashMap performance
cargo bench --bench concurrent_streams      # Parallel processor scaling
cargo bench --bench stream_topologies       # Stream combining & sharding ⭐
cargo bench --bench end_to_end             # Complete CSV pipeline
cargo bench --bench runtime_comparison      # Threading analysis

# Quick smoke test (faster, less accurate)
cargo bench -- --quick

# Save baseline for regression testing
cargo bench -- --save-baseline main

# Compare against baseline after changes
cargo bench -- --baseline main

Viewing Results

Criterion generates detailed HTML reports:

# Open benchmark report in browser
open target/criterion/report/index.html

Performance Summary

See benches/BENCHMARKS.md for comprehensive documentation including:

  • Detailed benchmark descriptions
  • Performance targets and expectations
  • Interpreting Criterion output
  • Regression testing setup
  • Profiling integration (flamegraph, valgrind)

Current Performance Metrics

Full results: See BENCHMARK_RESULTS.md for complete data.

Key Performance Highlights:

Metric Performance Analysis
Single-threaded processing 22M tx/sec Exceptional baseline throughput
8-shard parallel 5.4M tx/sec (2.6x speedup) Effective parallelization
Concurrent streams (10,000) 46.5M tx/sec aggregate Near-perfect scaling
End-to-end CSV pipeline 1.89M tx/sec CSV parsing adds ~40% overhead
Chain vs Merge Chain: 3.0M, Merge: 2.4M Sequential faster for small streams
Storage operations (DashMap) 700K-37M ops/sec Excellent concurrent performance

Analysis:

  • Exceptional raw processing: 22M tx/sec single-threaded mixed workload
  • Strong scaling: 2.6x speedup with 8 shards demonstrates efficient parallelization
  • Massive concurrency: 46.5M tx/sec aggregate with 10K concurrent streams
  • Topology flexibility: Chain and Merge combinators for different use cases
  • ✅ Successfully validates "thousands of concurrent TCP streams" architectural claim
  • ℹ️ End-to-end limited by CSV parsing overhead (~40%), not core processing

Tokio Runtime Threading Impact

Comparison of single-threaded vs multi-threaded Tokio runtime (100 concurrent streams benchmark):

Thread Count Time Throughput Speedup
1 thread 596 µs 16.8M tx/sec 1.0x (baseline)
8 threads 187 µs 53.5M tx/sec 3.2x ✅ Optimal
64 threads (default) 339 µs 29.5M tx/sec 1.8x

Key Insights:

  • 8 threads is optimal for small-to-medium workloads (< 1,000 streams)
  • ⚠️ 64 threads adds overhead for small workloads due to thread coordination costs
  • Large workloads (10K+ streams) likely benefit from more threads
  • ℹ️ Default Runtime::new() uses 64 threads (matching CPU core count)

Recommendation: For maximum performance, tune thread count based on expected concurrency. Production deployments with 1,000+ concurrent connections should use all available cores.

See RUNTIME_ANALYSIS.md for detailed threading analysis.

Performance Profiling

The project includes function-level profiling using the hotpath crate to identify performance bottlenecks and validate optimization opportunities.

Profiling Tools

Six profiling binaries are available in the hotpath/ folder to test different scenarios:

# Baseline profiles
cargo run --release --bin hotpath_single_threaded --features profiling
cargo run --release --bin hotpath_multi_threaded --features profiling

# Stress test profiles
cargo run --release --bin hotpath_high_contention --features profiling      # Zipf distribution (80/20)
cargo run --release --bin hotpath_workflow_stress --features profiling      # Heavy dispute workflows
cargo run --release --bin hotpath_store_intensive --features profiling      # 60% transaction store ops
cargo run --release --bin hotpath_sparse_accounts --features profiling      # Realistic sparse account IDs ⭐

# All outputs saved to hotpath/output/*.txt

Profiling Results Summary

Full analysis: See hotpath/notes/PROFILING_ANALYSIS.md for complete breakdown.

Scenario Throughput Deposit Avg Key Insight
Single-threaded 7.0M tx/sec 121ns Sequential IDs, pure sync processing
Multi-threaded 6.3M tx/sec 336ns 100 streams, 8 threads
Sparse IDs (realistic) 5.6M tx/sec 389ns Production baseline with realistic account IDs
High contention (zipf) 6.6M tx/sec 359ns 80/20 access pattern
Workflow stress 7.9M tx/sec 404ns Heavy dispute/resolve/chargeback
Store intensive 14.6M tx/sec* 110ns 60% transaction store lookups

* Different transaction mix - not directly comparable

Note: Updated profiling results show improved single-threaded performance (7.0M vs previous 6.35M tx/sec) after StreamProcessor refactoring.

Critical Finding: Sequential account IDs (1, 2, 3...) used in most tests are 13% optimistic. Realistic sparse account IDs (simulating UUIDs/large random IDs) show 5.59M tx/sec, which is the true expected production performance.

Bottleneck Analysis

Primary finding: Multi-threading overhead (NOT contention) ✅

Comprehensive profiling across 6 scenarios with correct analysis of sparse, non-overlapping accounts:

  • Multi-threading overhead: 2.6-2.9x per-operation cost - EXPECTED for concurrent data structures
  • NOT account contention: Zipf tests + sparse IDs + disjoint clients prove minimal lock conflicts
  • Transaction store: Only ~8% overhead even with 60% store lookups - NOT A BOTTLENECK
  • Sparse account IDs: 13% performance degradation vs sequential IDs - proves realistic testing matters
  • Original assumptions: Both "transaction store" and "account contention" hypotheses DEBUNKED

Key Insight: With sparse accounts and low contention, the 2.6-2.9x overhead is from:

  • Lock/unlock overhead (even uncontended: ~20-50ns)
  • Atomic operations (~10-30ns)
  • Cache coherency protocol (~50-100ns)
  • Memory fences (~5-10ns)

Conclusion: Current architecture is already optimal. The overhead is the expected cost of thread-safe data structures.

Production Considerations ⚠️

In a production environment, the transaction store implementation would differ significantly:

  1. Persistence: Transactions stored in database (PostgreSQL, ScyllaDB) or append-only log (Kafka)
  2. Size constraints: Cannot keep all transactions in memory (billions of records)
  3. Typical solutions:
    • Write-ahead log for durability
    • LRU cache for recent transactions (hot data)
    • Database query for historical lookups (cold data)
    • Event sourcing with snapshots

The current in-memory DashMap implementation is optimized for simplicity and demonstration purposes but would be replaced with a durable, scalable storage backend in production.

Documentation

Code Quality

Linting & Formatting

# Check for warnings (zero tolerance)
cargo clippy --all-targets -- -D warnings

# Format code
cargo fmt

# Check formatting without modifying
cargo fmt -- --check

Current Status: ✓ Clippy clean, ✓ Formatted

Performance Characteristics

  • Time Complexity: O(n) where n = number of transactions
  • Space Complexity: O(c + t) where c = clients, t = deposits (for dispute resolution)
  • Concurrency: Lock-free reads, per-shard write locks
  • Streaming: Constant memory regardless of input size

Future Enhancements

Planned (Documented, Not Implemented)

  1. Property-Based Testing: Use proptest to verify invariants hold for all inputs
  2. Tracing Instrumentation: Add spans for profiling hot paths
  3. Performance Scaling:
    • Current: 5.6M tx/sec with 100 streams (sparse account IDs)
    • Target: 46M tx/sec with 10,000 streams (8x improvement)
    • Status: ✅ Already validated in benchmarks - configuration-only change
    • Conclusion: Current architecture is optimal; no code changes needed
  4. Deterministic Output: Sort accounts by client_id (currently non-deterministic)
  5. Granular Error Messages: Include line numbers in CSV parse errors

Stream Processing Topologies

The StreamProcessor API provides flexible topology configuration for processing multiple streams:

Single Stream (Simple Case):

use pay::prelude::*;
use std::sync::Arc;

let account_manager = Arc::new(ConcurrentAccountManager::new());
let transaction_store = Arc::new(ConcurrentTransactionStore::new());

StreamProcessor::new(account_manager, transaction_store, SkipErrors)
    .add_stream(csv_stream)
    .process()
    .await;

Multiple Streams (Sequential Processing):

// Process streams one after another - useful when order matters
StreamProcessor::new(account_manager.clone(), transaction_store, SkipErrors)
    .add_stream(main_transactions)
    .add_stream(corrections)
    .add_stream(adjustments)
    .with_stream_combinator(StreamCombinator::Chain)
    .process()
    .await;

Multiple Streams (Concurrent Processing):

// Process streams concurrently - maximizes throughput when order doesn't matter
StreamProcessor::new(account_manager.clone(), transaction_store, SkipErrors)
    .add_stream(region_a_stream)
    .add_stream(region_b_stream)
    .add_stream(region_c_stream)
    .with_stream_combinator(StreamCombinator::Merge)  // Default
    .process()
    .await;

Parallel Processing with Sharding:

// Scale to thousands of streams with parallel sharding
StreamProcessor::new(account_manager.clone(), transaction_store, SilentSkip)
    .with_shards(8)  // 8 parallel processing threads
    .with_shard_assignment(ShardAssignment::RoundRobin)  // Distribute evenly
    .add_stream(stream_1)
    .add_stream(stream_2)
    // ... add more streams
    .add_stream(stream_100)
    .with_stream_combinator(StreamCombinator::Merge)
    .process()
    .await;

Server Embedding Example:

use pay::prelude::*;
use std::sync::Arc;

async fn process_concurrent_tcp_connections(
    streams: Vec<impl Stream<Item = Result<Transaction<FixedPoint>, IoError>> + Send + 'static>
) {
    let account_manager = Arc::new(ConcurrentAccountManager::new());
    let transaction_store = Arc::new(ConcurrentTransactionStore::new());

    let mut processor = StreamProcessor::new(
        account_manager.clone(),
        transaction_store,
        SkipErrors,
    );

    // Add all incoming TCP connection streams
    for stream in streams {
        processor = processor.add_stream(stream);
    }

    // Process with optimal parallelism
    let results = processor
        .with_shards(8)  // 8 parallel processors
        .with_stream_combinator(StreamCombinator::Merge)  // Concurrent I/O
        .process()
        .await;

    // Check results
    if results.all_succeeded() {
        println!("All {} streams processed successfully", results.total_streams);
    }

    // Snapshot is thread-safe and non-blocking
    let mut output = Vec::new();
    write_snapshot(&*account_manager, &mut output).await.unwrap();
}

Dependencies

Production

  • serde: Serialization/deserialization
  • csv: Synchronous CSV (unused, kept for compatibility)
  • csv-async: Async CSV streaming
  • thiserror: Ergonomic error types
  • tokio: Async runtime with full features
  • tokio-util: Compatibility layer (compat feature)
  • futures: Stream traits and utilities
  • dashmap: Concurrent HashMap
  • async-trait: Async methods in traits
  • pin-project-lite: Pin projection (for Stream impl)
  • tracing: Zero-cost observability framework
  • tracing-subscriber: Log formatting (development)

Development

  • tempfile: Temporary files for tests
  • tokio-test: Tokio testing utilities
  • proptest: Property-based testing (planned)

Project Structure

pay/
├── src/
│   ├── domain/           # Business logic (pure functions)
│   │   ├── amount.rs     # Fixed-point arithmetic
│   │   ├── account.rs    # Account state & invariants
│   │   ├── transaction.rs # Transaction types
│   │   ├── operations.rs  # Pure business operations
│   │   └── error.rs      # Domain errors
│   ├── storage/          # Account storage abstractions
│   │   ├── traits.rs     # Storage interfaces
│   │   ├── concurrent.rs # DashMap implementation
│   │   ├── concurrent_transaction_store.rs # Dispute resolution
│   │   └── error.rs      # Storage errors
│   ├── engine/           # Transaction processing
│   │   ├── processor.rs  # Orchestrates domain + storage
│   │   └── error.rs      # Engine errors
│   ├── io/               # CSV reading/writing
│   │   ├── csv_reader.rs # Async CSV stream
│   │   ├── csv_writer.rs # Snapshot writer
│   │   ├── parse.rs      # CSV → Transaction parsing
│   │   └── error.rs      # IO errors
│   ├── streaming/        # Stream processing & topologies
│   │   ├── processor.rs  # StreamProcessor (main API)
│   │   └── error.rs      # Error policies (SkipErrors, AbortOnError, SilentSkip)
│   ├── app/              # Application layer
│   │   ├── cli.rs        # Reusable CLI abstraction
│   │   └── error.rs      # Unified error type
│   ├── prelude.rs        # Convenient imports
│   ├── lib.rs            # Library root
│   └── main.rs           # CLI entry point
├── benches/              # Performance benchmarks (Criterion)
│   ├── README.md         # Benchmark documentation
│   ├── src/              # Benchmark sources
│   │   ├── common/       # Shared benchmark utilities
│   │   ├── transaction_processing.rs  # Single-threaded baseline
│   │   ├── storage_operations.rs      # DashMap performance
│   │   ├── concurrent_streams.rs      # Parallel processor scaling
│   │   ├── stream_topologies.rs       # Topology comparisons ⭐
│   │   ├── end_to_end.rs              # Real-world CSV pipeline
│   │   └── runtime_comparison.rs      # Threading analysis
│   ├── fixtures/         # Test data
│   ├── notes/            # Analysis documentation
│   │   ├── BENCHMARK_RESULTS.md
│   │   └── RUNTIME_ANALYSIS.md
│   └── output/           # Generated outputs (gitignored)
├── hotpath/              # Function-level profiling
│   ├── src/                       # Profiling binaries (6 scenarios)
│   │   ├── single_threaded.rs     # Baseline single-threaded
│   │   ├── multi_threaded.rs      # Baseline multi-threaded
│   │   ├── high_contention.rs     # Zipf distribution (80/20)
│   │   ├── workflow_stress.rs     # Heavy dispute workflows
│   │   ├── store_intensive.rs     # Transaction store stress test
│   │   └── sparse_accounts.rs     # Realistic sparse account IDs ⭐
│   ├── notes/                     # Profiling documentation
│   │   ├── PROFILING_ANALYSIS.md  # Complete analysis (6 scenarios)
│   │   └── SETUP.md               # Quick start
│   ├── output/                    # Profile outputs (gitignored)
│   │   └── *_report.txt
│   └── README.md                  # Profiling guide
├── tests/
│   ├── fixtures/         # Sample CSV data
│   │   ├── simple.csv
│   │   ├── disputes.csv
│   │   └── errors.csv
│   └── integration_test.rs # End-to-end tests
├── BENCHMARK_RESULTS.md           # Performance metrics
├── RUNTIME_ANALYSIS.md            # Threading analysis
├── ai-usage.md           # Design decision documentation
├── Cargo.toml
└── README.md             # This file

Documentation

  • README.md (this file): Overview, usage, architecture
  • ai-usage.md: Detailed design discussions and rationale
  • Inline tests: Each module has comprehensive test coverage
  • Code comments: Focus on "why" over "what"

Design Principles & Quality Criteria

This implementation was built to demonstrate production-quality Rust code across multiple dimensions:

Criteria Implementation
Functionality ✓ Builds via cargo build, CLI interface, proper CSV I/O, all transaction types
Completeness ✓ All transaction types, disputes, resolves, chargebacks, account locking
Correctness ✓ 163 tests, sample data included, type system prevents invalid states
Safety & Robustness ✓ Error handling per layer, overflow checking, documented assumptions
Efficiency ✓ Streaming (constant memory), async I/O, configurable stream topologies
Maintainability ✓ Layered architecture, comprehensive docs, clean code over clever code
Performance ✓ 22M tx/sec single-threaded, 2.6x speedup with 8 shards, 46.5M aggregate with 10K streams
Observability ✓ Comprehensive benchmarks, topology comparisons, profiling infrastructure

Note: This implementation prioritizes correctness, safety, and future scalability over premature optimization. Design decisions are documented in ai-usage.md with full rationale and trade-off analysis.

About

High-performance async payment transaction engine in Rust with comprehensive profiling and benchmarks. Features: 5.6M tx/sec, fixed-point arithmetic, concurrent processing, full dispute workflows. Includes 6 profiling scenarios and extensive documentation.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •