DATA Engineering Blog 1: ETL Dữ Liệu Từ URL
Trong thế giới của hệ thống phân tán, ranh giới giữa một 'thuật toán thông minh' và một 'thảm họa vận hành' thường chỉ cách nhau đúng một lần deploy. Series này là tập hợp những bài học xương máu được đánh đổi bằng downtime và hệ thống sập, viết ra để ghi nhớ và giúp các kỹ sư đi sau không dẫm lại vết xe đổ. Hôm nay, chúng ta sẽ mổ xẻ một case study điển hình từ team Data Platform: Bài học tàn khốc về khoảng cách giữa ảo tưởng 'code chạy ngon trên máy em' và thực tế khắc nghiệt của một kiến trúc Enterprise.
Bài toán: Pipeline Tải & Giải Nén Dữ Liệu Từ URL
Yêu cầu nghiệp vụ: Đối tác upload một file manifest.json lên S3 Landing Bucket. File JSON này chứa một URL (Google Drive, OneDrive, hoặc Direct Link) trỏ tới một file ZIP chứa dữ liệu thô.
Hệ thống cần:
- Bắt sự kiện S3 khi file JSON xuất hiện.
- Tải file từ URL đó về.
- Giải nén và đẩy các file dữ liệu vào S3 Bronze Bucket.
Hồi 1: Cạm bẫy của Đệ quy (The Recursive Anti-Pattern)
Một kỹ sư Junior trong team đã tiếp cận bài toán bằng một giải pháp "tự chế" (hacky workaround). Cậu ấy nhận ra rằng file ZIP từ URL có thể lên tới vài GB, trong khi AWS Lambda bị giới hạn thời gian thực thi tối đa là 15 phút (900s).
Thiết kế của Junior: > "Trong lúc Lambda tải và giải nén, em dùng timer để theo dõi. Nếu chạm mốc 14 phút mà chưa xong, Lambda sẽ đóng gói state hiện tại, rồi gọi boto3.client('lambda').invoke() để tự trigger chính nó, mở một process mới chạy tiếp phần dang dở."
Dưới góc độ của một coder, đây là một tư duy linh hoạt. Dưới góc độ của một Architect, đây là một quả bom nổ chậm.
- Rủi ro chi phí (Infinite Loop): Nếu file ZIP bị hỏng (corrupted) khiến quá trình giải nén liên tục ném ra Exception, Lambda sẽ tự gọi lại chính nó đến vô tận. Hóa đơn AWS cuối tháng sẽ là một thảm họa.
- Mất kiểm soát trạng thái (State Leak): Truyền state qua payload của hàm đệ quy rất dễ dẫn đến thất thoát dữ liệu (data loss) hoặc nhân bản dữ liệu (data duplication) nếu một process bị crash giữa chừng.
Hồi 2: Ba Bức Tường Của Môi Trường Enterprise
Ngay cả khi bỏ qua vấn đề đệ quy, khi đoạn code xử lý URL này được đẩy lên môi trường Production, nó lập tức vỡ vụn trước 3 rào cản đặc trưng của kiến trúc Enterprise:
1. Giới hạn tài nguyên (Memory Exhaustion)
Code ban đầu dùng io.BytesIO(response.read()) để tải toàn bộ URL vào RAM.
- Vấn đề: Với file ZIP 2GB và Lambda được cấp 512MB RAM, hệ thống lập tức sập với lỗi
Out of Memory (OOM). - Giải pháp: Phải áp dụng kỹ thuật Streaming. Tải luồng dữ liệu và ghi thẳng xuống ổ cứng tạm
/tmpcủa Lambda (hỗ trợ tối đa 10GB). Tránh tuyệt đối việc nạp các payload lớn vào bộ nhớ.
2. Cô lập mạng (VPC & Outbound Traffic)
Ở máy local, lệnh urllib.request.urlopen(url) chạy trơn tru. Trên Prod, nó báo Connection Timeout.
- Vấn đề: Lambda xử lý dữ liệu của công ty được đặt trong một Private Subnet (Mạng nội bộ) không có Public IP. Nó bị chặn hoàn toàn quyền truy cập trực tiếp ra Internet.
- Giải pháp: Yêu cầu DevOps định tuyến (route) traffic của Subnet đó qua một NAT Gateway. Kỹ sư phần mềm phải hiểu rõ Topology mạng nơi code của mình hoạt động.
3. Bảo mật đa lớp (IAM & Least Privilege)
Sau khi thông mạng, Lambda tiếp tục văng lỗi AccessDenied.
- Vấn đề: S3 Event Trigger chỉ làm nhiệm vụ "đánh thức" Lambda. Nó không cấp quyền cho Lambda đọc S3.
- Giải pháp: Áp dụng nguyên tắc Least Privilege. Viết IAM Policy chỉ định đích danh Action (
s3:GetObject,s3:PutObject) và Resource cụ thể (arn:aws:s3:::<bucket>/<prefix>/*). Không bao giờ dùng wildcards3:*trên Production.
Hồi 3: Tái thiết kế - Sức mạnh của sự "Nhàm chán" (Event-Driven Design)
Tôi đã yêu cầu team đập bỏ logic đệ quy và thiết kế lại theo chuẩn Cloud-Native. Trong hệ thống phân tán, sự ổn định đến từ việc chia tách các thành phần (Decoupling) chứ không phải từ những thuật toán cồng kềnh.
- Buffer bằng SQS: Thay vì S3 gọi thẳng Lambda, S3 sẽ bắn sự kiện vào hàng đợi SQS. Nếu đối tác up 1000 manifest cùng lúc, SQS sẽ giữ chúng lại để kiểm soát tải (Concurrency limit), bảo vệ hệ thống downstream.
- Fan-out Pattern: Nếu phát hiện file từ URL quá lớn, Lambda đầu tiên sẽ không tự tải. Nó đóng vai trò "Router", băm nhỏ tác vụ và gửi các URL con vào một SQS khác, hoặc kích hoạt một dịch vụ thiết kế cho Long-running task như AWS Fargate (ECS) hay AWS Batch.
- Dead Letter Queue (DLQ): Mọi lỗi không lường trước (URL chết, file zip lỗi) đều được SQS retry tự động. Sau 3 lần thất bại, message được chuyển an toàn vào DLQ để Data Ops xử lý thủ công, đảm bảo Zero Data Loss.
Kết luận
Để chuyển từ cấp độ Junior lên Senior/Architect, bạn cần ngừng việc "hack" giới hạn của framework. Thay vào đó, hãy hiểu rõ các ràng buộc về Hạ tầng, Mạng lưới và Bảo mật. Hệ thống xuất sắc nhất không phải là hệ thống dùng nhiều trick nhất, mà là hệ thống dễ vận hành, dễ debug và an toàn nhất.
All rights reserved