Top view mechanical tools arrangement

From 25 Minutes to 30 Seconds: Shell Script to Go Migration Success Story

Technologies used:
Shell Script Shell Script
Go Go
Google BigQuery Google BigQuery

In the fast-paced world of data engineering, performance bottlenecks can make or break your data pipeline. Recently, Code4 tackled a critical performance challenge that was causing significant delays in a client’s data processing workflow. What started as a 25-minute ordeal became a lightning-fast 30-40 second operation—a remarkable 50x performance improvement achieved through strategic technology migration and architectural redesign.

The Challenge: A Sluggish Shell Script with Sequential SQL Queries

Our client’s existing data processing system relied on shell scripts that executed sequential SQL queries against their MySQL database before pushing data to Google BigQuery for analytics. While shell scripts’ simplicity and direct database access made them an attractive choice initially, the sequential nature of the queries created a significant bottleneck.

The scripts executed one SQL query at a time, waiting for each operation to complete before moving to the next. What should have been a quick data synchronization task was taking an agonizing 25 minutes to complete, severely impacting the client’s ability to make timely data-driven decisions.

The Root of the Problem

Several factors contributed to the performance issues:

  • Sequential Query Execution: Shell scripts executed SQL queries one at a time, creating unnecessary wait times
  • Database Connection Overhead: Each query required individual database connections and network round trips
  • I/O Bound Operations: The scripts spent most of their time waiting for database responses rather than actively processing data
  • Resource Underutilization: Modern multi-core systems were effectively running single-threaded shell script operations

The Solution: Go + Parallelization

After analyzing the bottlenecks, our team proposed a complete rewrite using Go (Golang) with a focus on concurrent processing. Go’s excellent support for goroutines and channels made it the perfect choice for replacing the sequential shell scripts and SQL queries with a high-performance data processor.

Key Architectural Changes

Concurrent Query Execution: Instead of executing SQL queries sequentially from shell scripts, we implemented a worker pool pattern using goroutines. Multiple workers could execute different database queries simultaneously.

Connection Pooling: We established efficient database connection pools to minimize connection overhead and reuse existing connections across multiple operations.

Batch Query Operations: Rather than individual sequential SQL queries, we implemented batch operations that could handle multiple queries in a single transaction.

Channel-Based Communication: Go’s channels enabled safe communication between goroutines, allowing for coordinated query execution and result aggregation without race conditions.

Implementation Highlights

// Worker pool implementation for concurrent SQL query execution
func processQueriesConcurrently(queries []string, numWorkers int) {
    jobs := make(chan string, len(queries))
    results := make(chan Result, len(queries))
    
    // Start workers
    for i := 0; i < numWorkers; i++ {
        go queryWorker(jobs, results, dbPool)
    }
    
    // Send queries
    for _, query := range queries {
        jobs <- query
    }
    close(jobs)
    
    // Collect results
    for i := 0; i < len(queries); i++ {
        <-results
    }
}

The Results: Dramatic Performance Improvement

The migration delivered extraordinary results:

  • Execution Time: Reduced from 25 minutes to 30-40 seconds
  • Performance Gain: 50x improvement in processing speed
  • Resource Utilization: Full utilization of available CPU cores
  • Scalability: The new architecture can easily scale with increased data volumes

Technical Benefits Beyond Speed

Memory Efficiency

Go’s efficient memory management and garbage collection resulted in lower memory consumption compared to the shell script implementation, despite executing queries concurrently.

Reliability

The strong typing system and explicit error handling in Go reduced runtime errors and improved system reliability.

Maintenance

The compiled nature of Go eliminated the dependency and environment issues present with shell scripts—no more quoting nightmares, OS compatibility problems, or script fragility issues.

Monitoring

Better observability through Go’s built-in profiling tools helped identify and resolve performance bottlenecks quickly.

Database Integration Improvements

The MySQL integration was significantly enhanced through:

  • Parallel Query Execution: Multiple SQL queries executed concurrently instead of sequentially
  • Prepared Statements: Reduced query parsing overhead
  • Transaction Batching: Multiple operations grouped into single transactions
  • Connection Pool Management: Optimized connection reuse and lifecycle management
  • Query Optimization: Restructured queries to minimize database load and enable parallel execution

BigQuery Pipeline Enhancement

The improved performance had cascading effects on the entire data pipeline:

  • Real-time Analytics: Faster data ingestion enabled near real-time analytics capabilities
  • Cost Reduction: Reduced BigQuery slot usage due to more efficient data loading
  • Improved SLA: Better service level agreements with downstream consumers

Lessons Learned

Choose the Right Tool for the Job

While shell scripts excel at simple system administration and quick automation tasks, Go’s concurrency model made it superior for this I/O-intensive database operation processing task.

Architecture Matters More Than Language

The real performance gains came from moving from sequential SQL queries to concurrent query execution, not just the language change.

Measure Everything

Continuous profiling and monitoring helped identify bottlenecks and validate improvements throughout the migration process.

Plan for Scale

The new architecture was designed with future growth in mind, ensuring the system can handle increasing data volumes without degradation.

Conclusion

This migration project demonstrates the transformative power of choosing the right technology stack and architectural approach for specific use cases. By leveraging Go’s strengths in concurrent programming and carefully redesigning the SQL query execution pipeline, we achieved a 50x performance improvement that transformed a painful 25-minute operation into a smooth 30-40 second process.

The success of this project highlights Code4’s commitment to not just solving immediate problems, but engineering solutions that provide long-term value. When performance matters for database operations and data pipeline tasks, sometimes the best approach is to step back, reevaluate your architecture, and embrace the right tool for the job.

For organizations facing similar data processing challenges involving sequential shell scripts and SQL queries, this case study serves as a reminder that significant performance improvements are possible with thoughtful technology choices and architectural design. The investment in migration and redesign pays dividends in improved efficiency, reduced operational costs, and enhanced user experience.