2025, Oct 07 07:32
miniopy-async के साथ बड़े MinIO ऑब्जेक्ट्स को स्ट्रीमिंग से zip करें
4 GB RAM में 40 GB तक के MinIO ऑब्जेक्ट्स को सुरक्षित zip करें: miniopy-async से स्ट्रीमिंग डाउनलोड, टेम्प फ़ाइल पर आर्काइव लिखना, कम मेमोरी और भरोसेमंद अपलोड का तरीका.
कई बड़े MinIO ऑब्जेक्ट्स को एक ही आर्काइव में समेटना आसान लगता है—जब तक कि आंकड़े सामने न आ जाएँ. किसी‑किसी ऑब्जेक्ट का आकार 40 GB तक पहुँचता है, उपलब्ध RAM 4 GB है, और डिस्क स्पेस लगभग 240 GB. स्टैक में miniopy-async होने पर व्यावहारिक तरीका यही है कि पूरे पेलोड को मेमोरी में बफर करने से बचें और डाउनलोड तथा zip बनाने—दोनों—को स्ट्रीम करें।
जाल: मेमोरी में zip बनाना
अक्सर पहला ख्याल होता है कि ऑब्जेक्ट को पूरा पढ़कर फिर zip में लिख दें—और सख्त मेमोरी सीमा में यही तरीका टूट जाता है. नीचे दिया गया स्केच दिखाता है कि मल्टी‑GB ऑब्जेक्ट्स के लिए यह तरीका क्यों असुरक्षित है।
import io
import zipfile
import asyncio
from miniopy_async import Minio
api = Minio("localhost:9000", access_key="xxx", secret_key="xxx", secure=False)
async def build_zip_in_ram(source_bucket, item_keys):
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_STORED) as bundle:
        for name in item_keys:
            res = await api.get_object(source_bucket, name)
            blob = await res.read()  # पूरा ऑब्जेक्ट RAM में खींच लेता है
            bundle.writestr(name, blob)
            await res.close()
    return buf.getvalue()  # अब पूरा आर्काइव भी RAM में ही रहता है
यह संस्करण read() के साथ हर ऑब्जेक्ट डाउनलोड करता है, उसे पूरी तरह मेमोरी में रखता है और BytesIO बफर में आर्काइव बनाता है. 4 GB RAM और 40 GB तक के ऑब्जेक्ट्स के साथ, सीमा टकराना तय है।
समस्या का सार
मुख्य दिक्कत बफरिंग है. get_object और read() के जरिए बड़े ऑब्जेक्ट खींचने पर उनका आकार जितनी मेमोरी आवंटित होती है, और BytesIO में बढ़ते हुए zip को थामे रखना दबाव को कई गुना बढ़ा देता है. समाधान है पूरे पाइपलाइन में मेमोरी बफरिंग की जगह स्ट्रीमिंग अपनाना।
miniopy-async, get_object से एक async स्ट्रीम देता है, जिससे आते‑आते चंक्स को प्रोसेस किया जा सकता है. Python का zipfile फ़ाइल‑जैसे टारगेट्स के साथ काम करता है, पर उन्हें seek करने योग्य मानता है. सीधा और भरोसेमंद उपाय है कि आर्काइव को डिस्क पर अस्थायी फ़ाइल में लिखें—जब RAM की तुलना में डिस्क काफी अधिक हो, तो यह बाधाओं के अनुरूप होता है।
टेम्प फ़ाइल के साथ व्यावहारिक स्ट्रीमिंग समाधान
नीचे का इम्प्लीमेंटेशन MinIO से हर ऑब्जेक्ट को सीधे एक zip एंट्री में स्ट्रीम करता है, zip को डिस्क पर अस्थायी फ़ाइल में लिखता है, और अंत में उसी फ़ाइल को एकल ऑब्जेक्ट के रूप में MinIO में अपलोड कर देता है. प्रक्रिया में न तो पूरे ऑब्जेक्ट्स और न ही पूरा आर्काइव मेमोरी में रखा जाता है।
import asyncio
import aiofiles
import tempfile
import zipfile
from miniopy_async import Minio
mc = Minio("localhost:9000", access_key="xxx", secret_key="xxx", secure=False)
async def pack_and_push(src_bucket, object_ids, dst_bucket, dst_name):
    # बनने वाले zip आर्काइव के लिए एक अस्थायी फ़ाइल बनाएँ
    with tempfile.NamedTemporaryFile(delete=False) as tf:
        spool_path = tf.name
    # हर ऑब्जेक्ट को बिना पूरी तरह RAM में लोड किए आर्काइव में स्ट्रीम करें
    with zipfile.ZipFile(spool_path, "w", compression=zipfile.ZIP_STORED) as archive:
        for obj_key in object_ids:
            stream = await mc.get_object(src_bucket, obj_key)
            with archive.open(obj_key, "w") as sink:
                async for fragment in stream.stream():
                    sink.write(fragment)
            await stream.close()
    # तैयार zip फ़ाइल को वापस MinIO में अपलोड करें
    await mc.fput_object(dst_bucket, dst_name, spool_path)
यह पैटर्न मेमोरी उपयोग को किसी भी समय ट्रांसफर और लिखे जा रहे एकमात्र चंक के आकार तक सीमित रखता है, जबकि zipfile डिस्क पर seek करने योग्य आर्काइव लिखता है. 240 GB ड्राइव के साथ, जब तक अंतिम zip डिस्क में समा जाए, आप बड़े आर्काइव आराम से बना सकते हैं।
जब डिस्क पर्याप्त नहीं होती
यदि N ऑब्जेक्ट्स का संयुक्त आकार डिस्क पर उपलब्ध स्थान से बड़ा हो जाए, तो अगला कदम है zip को सीधे MinIO में मल्टीपार्ट अपलोड के साथ स्ट्रीम करना. चूँकि zipfile को seek करने योग्य टारगेट चाहिए, इसके लिए स्टैंडर्ड लाइब्रेरी के बजाय स्ट्रीमिंग zip इम्प्लीमेंटेशन चाहिए. एक विकल्प zipstream जैसी लाइब्रेरी है, जो स्ट्रीम के रूप में zip पैदा करती है।
मैदान से नोट्स
मैं इसे समाधान के रूप में चिह्नित करूँगा. दूसरा तरीका सबसे तर्कसंगत है. लेकिन मेरे पास एक और विचार है — .read() के बाद io.BytesIO को ब्लॉक करना ताकि नए डेटा का इंतजार किया जा सके, यह ऑब्जेक्ट को क्रमिक रूप से अपलोड करने में मदद करेगा. लेकिन यह अभी सिद्धांत है.
ऊपर का विचार भी उसी दिशा की ओर इशारा करता है: पूरी पेलोड मेमोरी में जमा करने से बचें, अनुक्रमिक प्रवाह को प्राथमिकता दें. व्यवहार में, दी गई सीमाओं के तहत डिस्क‑समर्थित टेम्प फ़ाइल सबसे सीधा और पूर्वानुमेय तरीका रहती है।
यह क्यों मायने रखता है
बड़े‑ऑब्जेक्ट प्रोसेसिंग पाइपलाइन्स अक्सर बैंडविड्थ के कारण नहीं, बल्कि अनियंत्रित बफरिंग के कारण विफल होती हैं. स्ट्रीमिंग पीक मेमोरी को न्यूनतम रखती है, सीमित RAM में स्थिरता बनाए रखती है, और आर्काइविंग जैसी अन्यथा सरल कार्रवाइयों के दौरान आकस्मिक OOM घटनाओं से बचाती है. seek करने योग्य टेम्प फ़ाइल का उपयोग, zip फ़ॉर्मैट की आवश्यकताओं को दरकिनार करते हुए अपलोड फ्लो को जटिल किए बिना समाधान देता है।
निष्कर्ष
यदि आपको miniopy-async के साथ सीमित मेमोरी वाले होस्ट पर कई बड़े MinIO ऑब्जेक्ट्स को zip करना है, तो जितना संभव हो उतना स्ट्रीम करें. read() की जगह get_object से आने वाले चंक्स पर इटरेट करें, zip एंट्रीज़ को सीधे डिस्क पर अस्थायी फ़ाइल में लिखें, फिर तैयार फ़ाइल को fput_object से अपलोड करें. अगर अंतिम आर्काइव डिस्क में नहीं समाता, तो स्ट्रीमिंग zip इम्प्लीमेंटेशन और मल्टीपार्ट अपलोड पर स्विच करें. मल्टी‑GB ऑब्जेक्ट्स के लिए इन‑मेमोरी बफर से दूर रहें—आपकी पाइपलाइन तेज़ भी रहेगी और अनुमानित भी।