StoreFleet
Trang chủBlog › Shopify Bulk Operations API cho nhiều store

Shopify Bulk Operations API cho nhiều store

Cách Bulk Operations API thay cho vòng lặp update bị throttle trên nhiều store Shopify — mô hình async, cách tôi chuyển đổi, và chỗ vẫn còn đau.

Linh Nguyen · Cập nhật

Điểm chính — AI tóm tắt
  • Vòng lặp update đồng bộ trên Admin API chạy ngon cho tới khi hết ngon — ở vài nghìn sản phẩm trải trên vài store, nó bị throttle, kéo lê hàng giờ, và có thể chết giữa chừng mà không có cách nào sạch để biết cái gì đã đổi rồi
  • Bulk Operations API lật ngược mô hình — bạn upload một file JSONL mỗi dòng là một mutation, submit bằng bulkOperationRunMutation, rồi Shopify chạy async phía nó và trả lại file kết quả (cơ chế theo shopify.dev), thay vì bạn ngồi trông vài nghìn call trực tiếp
  • Từ API version 2026-01, một shop chạy được tối đa năm bulk mutation cùng lúc (trước đó một per type), đổi cách bạn rải việc ra cả fleet — nên xác nhận lại giới hạn hiện hành trên shopify.dev trước khi dựng kiến trúc quanh nó
  • Bulk ops không phải hết đau — staged upload, một completion window, xử lý lỗi từng dòng trong JSONL kết quả, và không phải mutation nào cũng bulk-eligible, nghĩa là vẫn phải tự viết error handling tử tế
  • Khi CSV quá cứng còn vòng lặp thường thì gãy, bulk ops là tầng giữa đúng — đó là tầng mà bộ công cụ đa store của chính chúng tôi đẩy thay đổi sản phẩm, giá, tag qua

AI tổng hợp từ chính nội dung bài viết; tác giả đã soát lại.

Trong bài này
  1. Tại sao vòng lặp thấy ổn cho tới khi hết ổn
  2. Mô hình tư duy làm mọi thứ sáng ra
  3. Chuyện chuyển đổi, kể cả chỗ tôi làm sai
  4. Chỗ tôi thực sự vạch ranh giới bây giờ

Tháng 5/2026, tôi ngồi nhìn một cái terminal cập nhật khoảng 4.000 sản phẩm trên ba trong số các store chúng tôi vận hành — một đợt đổi giá toàn nhà cung cấp cộng với dọn tag — và tôi nhẩm tính theo thanh tiến độ. Với tốc độ bò đó, nó sẽ ngốn gần hết buổi chiều. Rồi tới tầm 1.200 sản phẩm, một store bắt đầu trả về 429 Too Many Requests, logic retry của tôi lùi lại, lùi thêm lần nữa, và cả run đứng hình. Tệ hơn: tôi không còn biết, trừ khi query lại từng sản phẩm, cái nào đã được cập nhật và cái nào chưa. Một đợt đổi giá áp được nửa vời trên một store đang bán hàng không phải kiểu mập mờ bạn muốn có lúc 3 giờ chiều.

Buổi chiều đó là lý do cuối cùng tôi dời đường update hàng loạt ra khỏi vòng lặp API đồng bộ và chuyển sang Bulk Operations API của Shopify. Đây là bài mà tôi ước gì đã đọc trước: tại sao vòng lặp gãy ở quy mô lớn, mô hình async thực sự chạy thế nào, việc chuyển đổi tốn tôi cái gì, và chỗ nào vẫn còn cắn. Nếu bạn đã đọc bài rate limit của Shopify API hoạt động ra sao trên nhiều store, hãy coi bài này là phần tiếp theo thực dụng — vẫn cái vấn đề throttle đó, nhưng là phiên bản bạn thôi vật lộn với rate limiter mà đi vòng qua nó hẳn.

Tại sao vòng lặp thấy ổn cho tới khi hết ổn

Vòng lặp đồng bộ là thiết kế đầu tiên ai cũng nghĩ ra, và tôi vẫn bênh nó cho việc nhỏ. Bạn đọc một danh sách sản phẩm, lặp qua, bắn một mutation mỗi sản phẩm, xử lý response. Với vài trăm món trên một store thì xong trước cả khi bạn kịp châm lại ly cà phê.

Nó sụp đổ trên hai trục cùng lúc, và đa store đụng cả hai. Trục một là khối lượng: mỗi món là một round-trip trực tiếp, nên 4.000 sản phẩm là 4.000 call tuần tự, mỗi call trả tiền độ trễ mạng và mỗi call rút bớt ngân sách rate limit của bạn. Trục hai là fan-out: chạy đúng job đó trên cả fleet thì giờ bạn đang xả nhiều bucket per-store song song — đúng cái áp lực dồn cộng mà tôi từng viết trước đây: mỗi store vẫn nằm trong giới hạn riêng của nó trong khi cả pool worker của bạn cháy đèn throttle khắp nơi cùng lúc.

Bạn có thể lấp liếm bằng backoff, jitter và một cái queue, và chúng tôi đã làm vậy một thời gian dài. Nhưng backoff không làm job nhanh hơn — nó cố tình làm chậm lại để ít gãy hơn. Và nó không giải quyết cái phần thực sự làm tôi sợ: một vòng lặp chạy dài chết giữa chừng để lại cho bạn một trạng thái không rõ. CSV export/import né được vấn đề trạng thái cho dữ liệu catalog — đó vẫn là thứ tôi với tới đầu tiên cho những chỉnh sửa sản phẩm thẳng thớm, và bài quản lý sản phẩm hàng loạt bằng CSV của chúng tôi nói rõ trần đó ở đâu. Nhưng CSV cứng: nó có hình dạng sản phẩm, mỗi store một file, và không diễn đạt được các mutation GraphQL tinh vi hơn. Khi CSV quá cùn còn vòng lặp quá dễ vỡ, bạn đã tới đúng chỗ của bulk operations.

Mô hình tư duy làm mọi thứ sáng ra

Tôi né bulk operations mấy tháng trời vì đã lướt qua docs và nghĩ "cái này chỉ là batching thôi mà". Không phải. Cái chuyển dịch làm tôi vỡ ra: bạn thôi thực hiện các thay đổi, và bắt đầu mô tả chúng.

Thay vì code của bạn chạy vài nghìn mutation trực tiếp, bạn viết một file JSONL duy nhất, mỗi dòng là một input unit — Shopify chạy mutation của bạn một lần trên mỗi dòng. Bạn upload file đó (trước tiên gọi stagedUploadsCreate để giữ chỗ lưu trữ và lấy credential upload), rồi submit nó bằng bulkOperationRunMutation. Shopify chạy toàn bộ async phía nó và, khi xong, trả lại kết quả dưới dạng file JSONL bạn tải về từ field url trên operation (cơ chế theo shopify.dev). Có một bản sinh đôi phía query, bulkOperationRunQuery, để kéo dataset lớn ra theo cùng cách.

Hệ quả quan trọng nhất với tôi: theo docs của Shopify, việc submit bulkOperationRunMutation không bị metered theo rate limit chuẩn như cách vài nghìn call lẻ của bạn bị. Bạn không còn ngồi trông một vòng lặp trực tiếp đấu với cái bucket rỉ nữa — bạn giao cả batch và Shopify hấp thụ nhịp độ. Cái run kéo cả buổi chiều, hay bị throttle của tôi biến thành: dựng một file, submit, poll trạng thái, tải kết quả.

Poll là chuyện nhỏ riêng của nó. Trước đây bạn query currentBulkOperation để lấy trạng thái; từ API version 2026-01 có bulkOperation(id:) để kiểm tra một cái cụ thể, và bạn có thể subscribe webhook bulk_operations/finish thay vì poll hẳn. Tôi chọn webhook để không có gì kẹt trong vòng poll — hãy xác nhận tên field hiện hành trên shopify.dev cho bản API version bạn pin, vì khu vực này mới đổi gần đây.

Chuyện chuyển đổi, kể cả chỗ tôi làm sai

Thứ đầu tiên tôi làm sai là giả định mọi thứ tôi làm trong vòng lặp đều có bản bulk tương đương. Không phải mutation nào cũng bulk-eligible, và có một ràng buộc được ghi rõ trong docs rằng một bulk mutation chỉ được chứa một connection field duy nhất. Cái payload lồng nhau ban đầu của tôi — cập nhật một sản phẩm cùng các biến thể và media của nó trong một phát — không dịch sang gọn được. Tôi phải trải phẳng việc ra thành nhiều lượt riêng. Đó không phải chú thích nhỏ; nó đổi cách tôi mô hình hóa cả job.

Bất ngờ thứ hai lại là bất ngờ tốt. Với giới hạn một-operation-mỗi-type cũ, tôi từng tưởng phải serialize bulk job theo từng store. Nhưng từ API version 2026-01, một shop chạy được tối đa năm bulk mutation cùng lúc (tăng từ một bulk operation mỗi type trước đó). Trên một fleet, điều này định hình lại scheduler — bạn rải được nhiều import ra mỗi store thay vì xếp hàng nối đuôi nhau. Tôi vẫn khuyên bạn kiểm chứng con số concurrency chính xác trên shopify.dev trước khi dựng scheduler quanh nó, vì đây đúng loại giới hạn hay bị chỉnh lại, và tôi không muốn bạn xây kiến trúc dựa trên trí nhớ của tôi.

Error handling cũng dời chỗ. Trong vòng lặp trực tiếp bạn bắt từng lỗi ngay tại chỗ. Với bulk ops, lỗi quay về nằm trong JSONL kết quả, từng dòng một — bạn parse file đã hoàn thành, tìm những dòng lỗi, và quyết định retry cái gì. Điều đó thực sự tốt hơn cho cái vấn đề trạng thái từng làm tôi bỏng: file kết quả chính là bản ghi việc đã xảy ra, nên "sản phẩm nào thực sự đã đổi" là một file bạn đọc, không phải một phỏng đoán bạn đặt. Nhưng bạn phải tự viết cái parser đó. Và Shopify áp một completion window 24 tiếng cùng trần kích thước file 100 MB trên mỗi operation — operation nào chạy quá cửa sổ đó sẽ bị dừng và đánh dấu failed — nên với fleet rất lớn thì bạn vẫn phải chia việc thành nhiều file dù thế nào. Bulk ops không xóa bỏ vấn đề batching — nó dời vấn đề đó tới chỗ tôi lý luận được.

Chỗ tôi thực sự vạch ranh giới bây giờ

Sau khi chuyển đổi, nguyên tắc ngón tay cái của tôi cho đường update trên các store chúng tôi vận hành là một quy tắc ba tầng, và tôi chọn theo hình dạng của job, không chỉ theo kích thước:

Đây là quan điểm thật của tôi, cái phải trả bằng một buổi chiều nhìn thanh tiến độ đứng hình mới có được: ở quy mô đa store, vòng lặp update đồng bộ không phải phiên bản nhỏ hơn của cách làm đúng — nó là một cách khác, sai. Nó nhân các failure mode lên đúng theo khối lượng công việc. Mô hình async trông có vẻ nhiều bộ phận chuyển động hơn ở ngày đầu (staged upload, JSONL, webhook, một cái parser kết quả) và đúng là vậy. Nhưng mỗi bộ phận đó tồn tại để gỡ bỏ đúng cái mập mờ mà một vòng lặp đang chết để lại, và ở ba, năm, mười store thì cái mập mờ đó là thứ đắt đỏ. Giờ tôi không chạy những đợt đổi sản phẩm lớn toàn fleet theo cách nào khác nữa.

Quản lý hàng chục store Shopify trên một dashboard

Nhắn ngay trong khung chat trên trang — AI agent và đội ngũ trả lời tại chỗ — hoặc gửi email. Demo miễn phí trên chính store Shopify của bạn, chưa cần tạo tài khoản.