⚡ धडा 04 — जलद pipelines: cache, parallel jobs, matrix
📍 तुम्ही इथे आहात: 12 पैकी धडा 04 · मागे: lesson-03-first-pipeline · पुढे: lesson-05-artifacts-reports
📦 या ब्रँचमध्ये काय आहे
धडे 01–03, आणि courier ची फेरी छोटी ठेवणारी तीन साधने: inputs वरून किल्ली दिलेला ड्रॉवर, एकाच वेळी अनेक डेस्क, आणि तीन प्रकारे नक्कल केलेला एक डेस्क. खऱ्या files:
- .github/workflows/ci.yml —
hashFiles('app/prepare.js')वरून key दिलेलीactions/cache@v4step, hit झाल्यावर सावकाश step वगळणारेif:,fail-fast: falseसहstrategy.matrix - app/prepare.js —
npm ciऐवजी मुद्दाम सावकाश ठेवलेला पर्याय; त्याचा output आधीच असेल तर "already exists" छापतो. त्याचे output folderapp/prepared/.gitignore मध्ये आहे - .circleci/config.yml, .gitlab-ci.yml, Jenkinsfile — तोच ड्रॉवर आणि त्याच फूटपट्ट्या इतर बोलींमध्ये
🧒 5 वर्षांच्या मुलाला समजावल्यासारखे
प्रत्येक फेरीत, तपासणी डेस्कवरचा कारकून तुमच्या गृहपाठाला हात लावण्याआधी पेन्सिलींचा नवा बॉक्स तासायचा. त्याच पेन्सिली, तेच शार्पनर. ✏️
म्हणून mailroom ने एक ड्रॉवर 🗄️ जोडला. त्याचे label "पेन्सिली" नाही — ते तासण्याच्या सूचनांचा ठसा आहे. त्याच सूचना → तेच label → ड्रॉवर उघडा, पेन्सिली घ्या, तासणे वगळा: cache hit ⚡. नव्या सूचना → नवे label → असा ड्रॉवर नाही → तासा, मग नव्या पेन्सिली नव्या label खाली ठेवा: cache miss ⏳. रात्री शिपायाने ड्रॉवर रिकामा केला तरी काहीच हरवत नाही — पुढची फेरी पुन्हा तासते. धडा 05 च्या पाकिटापेक्षा 📎 हाच संपूर्ण फरक आहे.
वेग वाढवण्याच्या आणखी दोन युक्त्या. एकाच वेळी अनेक डेस्क 🪑🪑: एकमेकांवर
अवलंबून नसलेले jobs शेजारी शेजारी चालतात. आणि तीन फूटपट्ट्या 📏: तोच
डेस्क तीनदा नक्कल केलेला, प्रत्येक वेगळ्या Node version ने मोजणारा.
fail-fast बंद असेल तर एकाने चुकीचे मोजले तरी तीनही पूर्ण होतात.
🗺️ आकृती
flowchart LR
src["📄 app/prepare.js"]
key["🔑 key<br/>prepared-hash of prepare.js"]
look["🗄️ drawer lookup"]
hit["⚡ HIT<br/>restore app/prepared, skip prepare"]
miss["⏳ MISS<br/>node prepare.js pays the hash rounds"]
save["💾 save under the key<br/>when the job ends"]
test["✅ node --test<br/>three rulers at once"]
src -->|"1 hashFiles"| key
key -->|"2 look up"| look
look -->|"3a found"| hit
look -->|"3b not found"| miss
miss -->|"4 post step"| save
hit -->|"5 then test"| test
miss -->|"5 then test"| test
❓ काय
- Cache = पुढचा run पुन्हा बनवू शकेल अशा गोष्टीची जपलेली copy, वेळ वाचवण्यासाठी ठेवलेली. प्रत्येक repository साठी वेगळी, काढून टाकली जाऊ शकते, हरवली तरी सुरक्षित असली पाहिजे.
- Cache key = label, जे contents ठरवणाऱ्या inputs च्या hash वरून बनते:
prepared-${{ hashFiles('app/prepare.js') }}.prepare.jsचा एक byte बदला → नवा hash → नवी key → miss. - Hit / miss = key अस्तित्वात आहे / नाही. Hit झाल्यावर तुमच्या steps आधी path
restore होतो; miss झाल्यावर job संपताना तो save होतो. Restore
keys = miss झाल्यावर वापरले जाणारे पर्यायी prefixes, सगळ्यात नवे आधी —
npm ciसाठी योग्य, सापडेल त्यावर विश्वास ठेवणाऱ्या step साठी चुकीचे. - Parallel jobs = एकाच वेळी वेगवेगळे काम;
needs:नसलेले jobs default नुसार एकाच वेळी चालतात. Matrix = प्रत्येक parameter साठी तोच job पुन्हा, प्रत्येक भाग स्वतःच्या runner वर.fail-fast: falseएक भाग fail झाला तरी बाकीचे पूर्ण होऊ देते.
🧠 Cache करायचे, की नाही?
| 🗄️ cache करा | 🚫 cache करू नका | |
|---|---|---|
| key contents चे पूर्ण वर्णन करते का? | हो — prepared-<hash of app/prepare.js> |
नाही — hash बाहेरच्या files वरही अवलंबून असणारे outputs |
| ते हरवले तर किंमत काय | वेळ | अचूकपणा, किंवा हरवलेली deliverable |
| उदाहरणे | ~/.npm, app/prepared, Docker layers |
secrets, संपूर्ण repo चा build output, नंतरच्या job ला लागणाऱ्या files (artifact, धडा 05) |
🤔 का
सावकाश pipeline कडे दुर्लक्ष होते: लोक commits एकत्र करतात, PR check वगळतात, किंवा
"ती अजून चालू असतानाच" merge करतात. सामान्य run चा बराचसा भाग तुमच्या tests चा नसतो
— नव्या मशीनवर त्याच गोष्टी पुन्हा तयार करण्याचा असतो. प्रामाणिक key असलेला cache
तो भाग काढून टाकतो; matrix एकाच्या वेळेत तीन तपासण्या चालवते; fail-fast: false
"Node 24 बिघडले" चे रूपांतर पूर्ण अहवालात करते. Inputs वरून key देण्याचा हाच नियम
Docker चा layer cache चालवतो — Docker शाळेचा
धडा 02 पाहा.
🔧 कसे (या repo मध्ये)
ड्रॉवर. ci.yml मध्ये cache step key अस्तित्वात
असेल तर app/prepared restore करते आणि, miss झाल्यावर, job ची शेवटची step चालल्यानंतर
post step मध्ये ते save करते:
- name: 🗄️ cache the slow preparation (lesson 04)
id: cache
uses: actions/cache@v4
with:
path: app/prepared
key: prepared-${{ hashFiles('app/prepare.js') }} # change prepare.js → new key → miss
- name: ⏳ prepare (slow on a miss, skipped on a hit)
if: steps.cache.outputs.cache-hit != 'true'
run: node prepare.js
id: cache मुळे पुढची step cache-hit वाचू शकते. Hit झाल्यावर सावकाश step
पूर्णपणे वगळली जाते; miss झाल्यावर ती prepare.js चालवते,
जी मुद्दाम सावकाश आहे:
const ROUNDS = Number(process.env.PREPARE_ROUNDS || 12_000_000); // ~3 s on a laptop, ~8–10 s on a CI runner
if (fs.existsSync(OUT)) {
console.log(`⚡ prepared/table.json already exists — nothing to do (this is what a cache hit feels like)`);
process.exit(0);
}
ते वेळ ढोबळ अंदाज म्हणून घ्या — hosted runner सहसा laptop पेक्षा सावकाश असतो आणि
प्रत्येक run मध्ये बदलतो; महत्त्वाचा आहे तो आकार. existsSync तपासणी लक्षात घ्या:
prepare.js disk वर जे असेल त्यावर विश्वास ठेवते, म्हणूनच या cache ला restore keys
नाहीत. restore-keys: prepared- पर्याय (उदाहरणापुरता, ci.yml मध्ये नाही) जवळपासच्या
miss ला जुनी table.json देईल आणि prepare.js म्हणेल "already exists". Restore keys
npm ci सारख्या अर्धवट निकाल पूर्ण करणाऱ्या steps साठी असतात.
तीन फूटपट्ट्या. Matrix job वर असते; प्रत्येक भाग स्वतःच्या runner वरचा पूर्ण job आहे, म्हणून तीनही शेजारी शेजारी चालतात:
strategy:
fail-fast: false # lesson 04: let all three rulers finish, even if one fails
matrix:
node: [20, 22, 24] # three rulers — the same homework checked three ways
fail-fast: false नसेल (default true आहे) तर पहिला लाल भाग बाकीचे दोन रद्द करतो;
compatibility matrix ला तीनही उत्तरे हवी असतात. याउलट
ship.yml पाहा, ज्याचे jobs needs: ने साखळीत जोडलेले
आहेत — वेगवेगळे काम, मुद्दाम parallel नाही.
Docker layers हा इथला दुसरा मोठा cache आहे: नव्या runner कडे एकही नसतो, म्हणून
धडा 07 मधला docker build प्रत्येक layer पुन्हा build करतो, जोपर्यंत तुम्ही cache
सोबत आणत नाही (पर्याय आहेत; layer cache काय पुन्हा वापरू शकतो आणि काय नाही हे
Docker शाळेचा धडा 02 सांगतो).
🔁 हेच CircleCI मध्ये
- restore_cache: # lesson 04: the drawer
keys:
- prepared-v1-{{ checksum "prepare.js" }}
- run:
name: ⏳ prepare (skipped when restored from cache)
command: node prepare.js
- save_cache:
key: prepared-v1-{{ checksum "prepare.js" }}
paths: [prepared]
Restore आणि save या स्पष्ट steps आहेत; prepare.js प्रत्येक फेरीत चालते आणि hit झाल्यावर
फक्त "already exists" छापते. v1- हा हाताने reset करण्याचा switch आहे; matrix workflows: खाली असते.
🦊 हेच GitLab CI मध्ये
parallel:
matrix: # lesson 04: three rulers
- NODE: ["20", "22", "24"]
cache: # lesson 04: the drawer, keyed by the file that defines the work
key:
files: [app/prepare.js]
paths: [app/prepared/]
cache: key: files: यादीतील files चा hash तुमच्यासाठी काढते (hashFiles ची कल्पना); restore आणि save script: च्या आजूबाजूला आपोआप होतात.
🎩 हेच Jenkins मध्ये
matrix { // lesson 04: three rulers, in parallel
axes { axis { name 'NODE'; values '20', '22', '24' } }
agent { docker { image "node:${NODE}-alpine" } }
// Jenkins has no built-in cache step: a persistent agent keeps the workspace
// (so prepared/ survives between builds); ephemeral agents need the Job Cacher plugin.
sh 'node prepare.js'
प्रामाणिक टीप: matrix directive built-in आहे; key असलेला cache नाही.
🧪 करून पाहा
# 0) feel a miss and a hit on your laptop
cd app
node prepare.js # ⏳ preparing… ✅ wrote prepared/table.json in N s (a few seconds, laptop-dependent)
node prepare.js # ⚡ already exists — nothing to do
git status --short # empty: app/prepared/ is git-ignored, so the drawer stays out of commits
cd ..
# 1) in your fork: a whitespace change to prepare.js → new hash → new key → MISS
latest() { gh run list --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId'; }
echo "" >> app/prepare.js && git commit -am "touch prepare.js (new cache key)" && git push
gh run watch "$(latest)" && gh run view "$(latest)" --log | grep -E "Cache not found|preparing|Cache saved"
# 2) push again WITHOUT touching prepare.js → same key → HIT, prepare step skipped
git commit --allow-empty -m "same key, expect a hit" && git push
gh run watch "$(latest)" && gh run view "$(latest)" --log | grep -E "Cache restored"
# 3) see the drawers GitHub keeps for your fork
gh cache list
⚠️ नेहमीच्या चुका
- cache ची key त्याच्या contents चे वर्णन न करणाऱ्या गोष्टीवर ठेवणे (ठरलेली string, branch चे नाव) — तुम्हाला कायम कालच्याच पेन्सिली मिळतात; त्याऐवजी inputs चा hash घ्या
- सापडेल त्यावर विश्वास ठेवणाऱ्या step ला
restore-keysजोडणे, जसेprepare.jsचेexistsSync— जवळपासचा miss गपचूप जुना hit बनतो - compatibility matrix साठी
fail-fastdefault वर ठेवणे — पहिली लाल फूटपट्टी बाकीच्या दोन रद्द करते आणि प्रत्येक push ला तुम्हाला फक्त एकाच version बद्दल कळते
⏭️ पुढे
ड्रॉवर पुन्हा बनवता येणाऱ्या गोष्टींसाठी आहे. पुढे: ज्या गोष्टी तुम्ही हरवू देऊ नयेत त्या — माणूस वाचतो ते प्रगतिपुस्तक 📋 आणि पुढचा डेस्क उघडतो ते पाकीट 📎.
git checkout lesson-05-artifacts-reports