
Scatter-Gather pattern
Bronislav Klučka, 26. 9. 2025 09:44
Scatter-Gather je další z rodiny návrhových vzorů, které se věnují separation of concern - oddělení zodpovědnosti. Hlavním přínosem scatter-gather vzoru je paralelizace - možnost vykonávat několik operací najednou a je tedy vhodný pro prostředí, jazyky, které paralelizaci nabízejí, mají thready, workery, nebo umí spustit nové procesy.
Jako vždy začneme ukázkami:
Příklad: spam filter
Spam filter už jsme dělali, ale podíváme na něj znovu trochu jinak.
Pravidla:
potřebujeme zkontrolovat obsah emailu proti spam filteru
potřebujeme zkontrolovat hlavičky emailu (DKIM, SPF, …)
potřebujeme zkontrolovat odesilatele
Pseudo-implementace - rules engine
interface Email {
text: string;
sender: string;
headers: string[];
}
interface SpamRule {
/**
* function accepting email and returning score modifier
* @param email
* @returns
*/
processEmail: (email: Email) => number;
}
class SpamEngine {
protected rules: SpamRule[] = [];
addRules(rules: SpamRule[]) {
this.rules.push(...rules);
}
execute(email: Email): boolean {
const score = this.rules.reduce((acc, rule) => {
return acc + rule.processEmail(email);
}, 0);
return score > 0;
}
}
class SpamText implements SpamRule {
processEmail(email: Email): number {
// process
return 1;
}
}
class SpamSender implements SpamRule {
processEmail(email: Email): number {
// process
return 1;
}
}
class SpamHeaders implements SpamRule {
processEmail(email: Email): number {
// process
return 1;
}
}
/************************************
* programm.mts
*/
const spamEngine = new SpamEngine();
spamEngine.addRules([
new SpamText(),
new SpamSender(),
new SpamHeaders(),
]);
console.log(spamEngine.execute({
sender: 'john.doe@company.com',
headers: ['from: john.doe@company.com'],
text: 'this is spam',
}));
Výše uvedená implementace je naprosto v pořádku, nicméně má jednu nevýhodu: procesy běží sériově jeden za druhým. V některých případech, např. v pipeline patternu je potřeba zachovat pořadí, nicméně v tomto na pořadí nezáleží a tam může scatter-gather zrychlit processing.
Scatter-Gather
Podstata scatter-gather patternu je v tom, že na místo toho, aby kód prováděl výpočet sám v hlavním vlákně deleguje výpočet na paralelní procesy.
Úprava příkladu, který jsme měli by byla velmi jednoduchá:
Pseudo-implementace - scatter gather
interface Email {
text: string;
sender: string;
headers: string[];
}
interface SpamRule {
/**
* function accepting email and returning score modifier
* @param email
* @returns
*/
processEmail: (email: Email) => Promise<number>
}
class SpamScatterGather {
protected rules: SpamRule[] = [];
addRules(rules: SpamRule[]) {
this.rules.push(...rules);
}
async execute(email: Email): Promise<boolean> {
// parallel processing
// process all requests in parallel and wait until completion to continue
const scores = await Promise.all(this.rules.map(rule => rule.processEmail(email)));
const score = scores.reduce((acc, cur) => {
return acc + cur;
}, 0);
return score > 0;
}
}
class SpamText implements SpamRule {
async processEmail(email: Email): Promise<number> {
// process
return 1;
}
}
class SpamSender implements SpamRule {
async processEmail(email: Email): Promise<number> {
// process
return 1;
}
}
class SpamHeaders implements SpamRule {
async processEmail(email: Email): Promise<number> {
// process
return 1;
}
}
/************************************
* programm.mts
*/
const spamSG = new SpamScatterGather();
spamSG.addRules([
new SpamText(),
new SpamSender(),
new SpamHeaders(),
]);
console.log(await spamSG.execute({
sender: 'john.doe@company.com',
headers: ['from: john.doe@company.com'],
text: 'this is spam',
}));
V závislosti na jazyce je to potom otázka náročnosti utilizace threadů/workerů/procesů.
Pro efektivní použití je nicméně důležité, aby se hlavní výpočetní zátěž nacházela v paralelizovaných procesech a ne v hlavním threadu vaší aplikace.