Slow deploy – šetříme infrastrukturu

90 milionů vs. 500

Představte si, máte 90 milionů klientských stanic a všechny se ihned po plošné aktualizaci mají nahlásit centrálnímu serveru pro specifický update. Na konci máte stroj, který zvládá max. 500 spojení za sekundu. To je 30 tisíc za minutu. Necelé dva miliony za hodinu a tak dále.

Ať počítám, jak počítám, server to nemůže přežít. Už jen proto, že těch cca 70 milionů z 90 milionů requestů by nejspíš normálně přišlo během prvních dvou hodin a zbytek by se nějak trousil.

Zaplatit prachy, nebo napsat script?

Možnosti, jak toto vyřešit jsem si nalajnoval dvě.

  1. Zaplatit prachy a naškálovat servery, které poběží hodinu v roce na plný výkon a pak se budou 364 dní mrcasit na 10% výkon.
  2. Napsat nějaký omezovač requestů.

Kdybych zvolil variantu 1, nadpis článku by byl: „Jak zbytečně naškálovat servery a promrcasit prachy klientovi“.

Jak omezit 90 milionů zařízení, které spolu navzájem nekomunikují, neví o sobě a chtějí se doptávat centrálního serveru? (PS: toto je jistě úloha, kterou musí řešit třeba tvůrci botnetů).

Vyšší matematika

90 milionů / 500 (výkon serveru za sekundu) = 180 000 serversekund

180 000 / 60 = 3000 serverminut a to je 50 serverhodin, což jsou dva serverdny.

Velká část klientských stanic je obsluhována lidmi a ti v čase od 23 do 5 ráno spíše spí, než že by zatěžovali servery. K 50 spočteným hodinám tedy přidám 2 * 6 hodin nočního klidu, celkem tedy dva a půl dne, zaokrouhlíme na celé dny, takže 3.

Potřebuji lineárně natáhnou deploy proces na 3 dny.

Bojový plán

Na vstupu máme 6 parametrů.

  1. Název úlohy (pro případ že bychom jich v reálném čase měli více)
  2. Čas, kdy má začít provádění úlohy (ideálně čas v budoucnosti)
  3. Délku deploymentu (Jak dlouho nám potrvá celý proces – 3dny)
  4. Min procentuální škála na začátku, tedy např 20% ihned
  5. Max procentuální škála pro terminaci, pak skip na 100%.
  6. Úloha, kterou to má udělat, tedy callback

Jak se to bude chovat?

Dle vstupních parametrů si definujeme procentuální rozsah, do kterého se daná koncová stanice musí trefit během slow deploy fáze. Generátorem náhodných čísel se do rozsahu pokusíme trefit. Když ano, vykonáme callback, když ne, neděláme nic.

Každou sekundou se reálný čas a čas definovaný jako konec fáze slow deploy procesu zužuje, čímž zvyšuje procentuální šanci na zařazení do procesu.

Informace o tom, zda již byla klientská stanice zařazena do procesu, se ukládá do cookie.

Poté, co vyprší limit, vždy callback vykonáme.

Script

Script jsem se pokusil rozepsat tak, aby dávaly smysl mé myšlenkové pochody. V reálné aplikaci je jednodušší.

function slowDeploy (name,deployDate,rangeMS,min,max,callback){
  // get current time in miliseconds (from external library)
  var cu = lib.time.current(); 
  // skip slowDeploy if time is bigger than rangeMS
  var doIt = ((deployDate*1+rangeMS*1)>cu);    
  if(doIt){
    // define cookie prefix
    var cp = "sd_";
    // define cookie name
    var cn = cp+name;
    // init empty variable to populate console log in the end and save computation time while drawing console log
    var report = "";
    // range
    var ra = max - min;
        ra = (ra<=0?1:(ra>=100?100:ra));
        report += ("Range: " + ra + " % | ");
    // diff
    var df = cu - deployDate;
        report += ("Diff: " + df/1000 + " s | ");
    // progress
    var pr = df/rangeMS*100;
        report += ("Progress: " + pr + " % | ");
        // normalize progress
        pr = (pr>=100?100:(pr<0?0:pr));
    // piece
    var pi = 100/ra;
        report += ("Piece: " + pi + " % | ");
    // relative gain
    var rg = pr/pi;
        report += ("Relative gain: " + rg + " % | ");
    // absolute gain
    var ag = min+rg;      
        report += ("Abslute gain: " + ag + " % | ");
    console.log(report);
    console.log("Current scale is " + df + " ms - " + df/1000 + " s - " + df/1000/60 + " m - " + df/1000/60/60 + " h - " + df/1000/60/60/24 + " d",1);

    // random percentage
    var rd = Math.random()*100;
        report = ("Random result is " + rd + " % | ");
    // decide if deploy or not
    var fn = (rd<ag?true:false);    
        report += (fn? "Is in slowDeploy :-)":"Is not in slowDeploy :-(");
    // read cookie register time (from external library)
    var cv = lib.cookie.get(cn); 
    console.log(report,1);
  }
  else {
    // slow Deployment is over limit
    fn = true;
  }    
  // Slow deploy enabled already
  if(cv!=""){
    // do delayed
    console.log("Register '" + name + "' through slow deploy",1);
    callback();
  }
  // decide if set
  else {
    if(fn===true){
      console.log("Register for first time '" + name + "' through slow deploy",1);
      lib.cookie.set(cn,deployDate,60);       
      callback();
    }
    else{
      console.log("Currently excluded '" + name + "' due to slowDeploy",1);
    }
  }
}

 

Alternativy

V průběhu času mě napadly ještě další způsoby, jak nasazování omezovat. Jedním z nich je vzít křivku průměrné denní zátěže serveru, k ní vytvořit křivku opačnou a ta by dále sloužila jako předpis pro omezování trafficu.

Hrubý omezovač

Prostě vím, že můj server zvládne max 30 000 requestů za minutu, takže od 22. hodin do 10. hodin dopoledne nebudu script nijak omezovat. Od 10. do 15. a od 19. do 22. jej ořežu na cca 80%.

Převrácený omezovač

Definuji si maximální zátěž a z ní spočítám relativní křivku vytížení serveru, která je opačná denní návštěvnosti. Hodnoty po hodinách zapíšu do pole a dle aktuálního času se dynamicky mění šance, že klientská stanice dostane zelenou ke kontaktování centrálního serveru. To reprezentuje žlutá křivka.

 

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..