Autor Zpráva
G3n3sis19
Profil
Prosím udělejte si čas na toto téma, neodcházejte jen kvůli velikosti, mám tu problém s jednou funkcí. Vždy když ji zavolám mi to spadne na max_execution_time.

vypadá takto
        function proc_user_autobid($bid)
        {
                $bids_before_visiting_needed_to_extend_product = (time()-$bid['time_end'])/10;
                $automats = $this->query("SELECT * FROM autobid WHERE product_id = ".$bid['id'].";");
                if ($this->count($automats))
                {
                        $bids = 0;
                        while($automat = $this->fetch($automats))               //PRVNI PRUBEH
                        {
                                $bids++;
                                $this->query("
                                INSERT INTO bid (
                                `id` ,
                                `product_id` ,
                                `bidder_name` ,
                                `bidder_id` ,
                                `bid_time`
                                )
                                VALUES (
                                '', '".$bid['id']."', '".$automat['username']."', '".$automat['uid']."', '".time()."'
                                );");
                                $bidder_username = $automat['username'];
                                $bidder_id = $automat['uid'];

                                if ($automat['max_bids'] == 1)
                                {
                                        $this->query("DELETE FROM autobid WHERE uid = '".$automat['uid']."' and product_id = '".$automat['product_id']."'");
                                }
                                $bid['last_bidder'] = $automat['username'];
                                $bid['time_end'] += 10;
                                $bid['price_now'] += 0.1*$bids;
                                $automat['max_bids'] -= 1; //ZMENSIME POCET MAX. BIDU o 1
                                if ($automat['max_bids'] != 1) //A POKUD MA JESTE NEJAKY AUTOBIDY, POKRACUJEM!
                                {
                                        $pokracovat += $automat['max_bids']; //MUZEME POKRACOVAT, $POKRACOVAT MA V SOBE MAX POCET PRIHOZU
                                        $hraci[] = array("uid" => $automat['uid'], "bids" => $automat['max_bids'], "username" => $automat['username']);
                                }
                        }

                        $preskoceno = array( );
                        if ($bid['time_end']<time() and $pokracovat) //A POKUD JE KONEC MENSI NEZ TIME() 
                        {
                                while($bid['time_end']<time() and $pokracovat) //TAK POKRACUJEM DOKUD MAME BIDY NEBO DOKUD SE KONEC AUKCE NEDOSTANE NAD NULU
                                {
                                        if ($pokracovat)
                                        {
                                                unset($preskoceno);
                                                echo memory_get_peak_usage();die();
                                                $preskoceno = array();
                                                unset($zkonceno);
                                                foreach($hraci as $id => $co) //TEN CO MU JESTE ZBYVA ZKONTROLUJEME, JESTLI NENI POSLEDNI
                                                {
                                                        if ($bid['last_bidder'] == $hraci[$id]['username']) //PRESNE TADY
                                                        {
                                                                die($bid['last_bidder']."-vs-".$hraci[$id]['username']);
                                                                if (in_array($hraci[$id]['username'], $preskoceno))
                                                                {
                                                                        //echo $hraci[$id]['username'];
                                                                        //die();
                                                                        $zkonceno = true;
                                                                        break;

                                                                }
                                                                else
                                                                {
                                                                        $preskoceno[] = $hraci[$id]['username'];
                                                                        continue;//A POKUD JE POSLEDNI, POKACUJEME K DRUHEMU
                                                                }
                                                        }
                                                        else
                                                        {
                                                                $bids++;
                                                                $this->query("
                                                                        INSERT INTO bid (
                                                                `id` ,
                                                                `product_id` ,
                                                                `bidder_name` ,
                                                                `bidder_id` ,
                                                                `bid_time`
                                                                )
                                                                VALUES (
                                                                '', '".$bid['id']."', '".$hraci[$id]['username']."', '".$hraci[$id]['uid']."', '".time()."'
                                                                );");
                                                                $bidder_username = $hraci[$id]['username'];
                                                                $bidder_id = $hraci[$id]['uid'];
                                                                $bid['last_bidder'] = $hraci[$id]['username'];
                                                                $bid['time_end'] += 30;
                                                                $bid['price_now'] += 0.1*$bids;
                                                                if ($hraci[$id]['bids'] == 1)
                                                                {
                                                                        $this->query("DELETE FROM autobid WHERE uid = ".$hraci[$id]['uid']." and product_id = '".$bid['product_id']."'");
                                                                        unset($hraci[$id]);
                                                                        break;
                                                                }
                                                                $hraci[$id]['bids'] -= 1; //ZMENSIME POCET MAX. BIDU o 1
                                                        }

                                                        if ($zkonceno)
                                                        {
                                                                $pokracovat = false;
                                                                break;
                                                        }
                                                }

                                                if ($zkonceno === true)
                                                {
                                                        $pokracovat = false;
                                                        continue;
                                                }
                                       }else{
                                                break;
                                        }
                                }
                        }

                        $this->query("UPDATE product SET 
                                last_bidder = '".$bidder_username."', 
                                last_bidder_id = '".$bidder_id."', 
                                time_end = ".$bid['time_end'].", 
                                price_now = ".$bid['price_now']." 
                                WHERE id = ".$bid['id']);
                }
                return $bid;
        }


k věci, co má dělat: Dělám aukční systém, kde je možné autopřihazování.
G3n3sis19
Profil
Ale když na tu stránku nikdo nepřistoupí jeden den, musí se to dorovnat a tak musím dopočítat kolikrát musí automat přihodit za člověka aby se čas zase prodloužil (příhozem se aukce prodlužuje)

Takže tohle udělá první průchod. Potom zkontroluje jestliže konec aukce je menší než time(), a pokud je tak započne ten while() a jede až do doby, než nikomu nezbydou příhozy nebo alespoň jeden, anebo se dostanou do situace že čas konce aukce je větší než time().

Avšak nevím proč, udělá se asi 1010 dotazů do databáze a sekne se to. (log mysql zde (poslední 2 inserty a potom mazání z autobid z důvodu málo příhozů)):

		 1099 Query	INSERT INTO bidmanie_bid (
								`id` ,
								`product_id` ,
								`bidder_name` ,
								`bidder_id` ,
								`bid_time`
								)
								VALUES (
								'', '5', 'martinek', '2', '1301186129'
								)
		 1099 Query	DELETE FROM bidmanie_autobid WHERE uid = 2 and product_id = ''
		 1099 Query	INSERT INTO bidmanie_bid (
								`id` ,
								`product_id` ,
								`bidder_name` ,
								`bidder_id` ,
								`bid_time`
								)
								VALUES (
								'', '5', 'martinflyff', '1', '1301186129'
								)
		 1099 Query	DELETE FROM bidmanie_autobid WHERE uid = 1 and product_id = ''

a konec, dál se to nehne. Fakt už nevím, zkoušel jsem různé obezličky jako tam jsou videt (
if ($zkonceno){break;}
)
padá to údajně na řádce kde je
$preskoceno = array();
, tak jak vidíte jsem si před to dal
echo memory_get_peak_usage();die();
a to mi dává 508268 (bajtů).

Budu rád za každou radu
TomášK
Profil
Pokud se tam provádí tisíc dotazů, tak je potřeba rozmyslet, jestli je to špatně navržené (a skutečně ten kód má provést tisíce dotazů) nebo jestli to není možné a pak zjistit, třeba debuggerem nebo ladícími výpisy, co ten kód opravdu dělá.

Ten kód je hodně nepřehledný, i když snaha o přehlednost se nedá upřít. Některé komentáře jsou zbytečné, např.
$automat['max_bids'] -= 1; //ZMENSIME POCET MAX. BIDU o 1
if ($bid['time_end']<time() and $pokracovat) //A POKUD JE KONEC MENSI NEZ TIME() 

Nemá smysl psát, co kód dělá - to je vidět, že se tam nějaký konec porovnává s time(), na to nepotřebuju komentář, zajímá mě, *proč* se to děje. Rovněž je dobré dodržovat další pravidla - např. maximální odsazení bude 4x-5x (deset je určitě moc). Pokud je potřeba víc, pak to zřejmě jde rozdělit do více funkcí a bude to čitelnější.

If je zbytečný, vždy bude splněn, $pokracovat se kontroluje v podmínce while cyklu.
 while($bid['time_end']<time() and $pokracovat) //TAK POKRACUJEM DOKUD MAME BIDY NEBO DOKUD SE KONEC AUKCE NEDOSTANE NAD NULU
                                {
                                        if ($pokracovat)
                                        {



$zkonceno se píše se s, není potřeba porovnávat explicitně s true, stačí jako o řádek výš if($skonceno). Tělo lze nahradit jedním break; Je také divné mít proměnné $skonceno a $pokracovat, zřejmě by to šlo sloučit do jedné proměnné.
  if ($zkonceno === true)
                                                {
                                                        $pokracovat = false;
                                                        continue;
                                                }


Zkus se věnovat i čitelnosti kódu, daleko lépe se v něm budou hledat chyby, investovaný čas se vyplatí. Co je špatně netuším, možná je tam nějaké nekonečná smyčka nebo to prostě jen nestihne.
Tori
Profil
G3n3sis19:
Tohle funguje jinak, než jak píšete v komentáři (ř.33):
if ($automat['max_bids'] != 1) //A POKUD MA JESTE NEJAKY AUTOBIDY, POKRACUJEM!
Pokud uživateli zbývá jen jedno automat.přihození, tak o řádek výš z něj uděláte 0 a tahle podmínka projde, přestože uživatel už nemůže přihazovat. Spíš bych dala: if max_bids > 0.

Mám pocit, že příčinou překročení čas.limitu je nekonečná smyčka díky chybě logiky:
Předpokládejme situaci, že v tabulce autobid je pouze jeden účastník aukce, který je zároveň posledním přihazujícím ($bid[last_bidder]).
// Kód osekán na nezbytnou kostru. Výchozí stav:
// V $hraci[0] je ten jediný přihazující automat. $pokracovat > 0, $bid[time_end] < time().

$preskoceno = array( );
while($bid['time_end']<time() and $pokracovat) 
{
    if ($pokracovat)
    {
      foreach($hraci as $id => $co) 
      {
          if ($bid['last_bidder'] == $hraci[$id]['username'])
          {
              // Tato podmínka se nesplní, $zkonceno je stále undefined (=> false)
              if (in_array($hraci[$id]['username'], $preskoceno))
              {
                  $zkonceno = true;
                  break;

              }
              else
              {
                  // je to jediný uživatel, takže pokračujeme až ZA foreach
                  $preskoceno[] = $hraci[$id]['username'];
                  continue; 
              }
          }
          else
          {
              $bids++;
              $this->query("INSERT INTO bid  ..... ");
              // K tomuto tedy nikdy nedojde, čas se nemění
              $bid['time_end'] += 30; 
          }
          
          // Podmínka se nesplní
          if ($zkonceno)
          {
              $pokracovat = false;
              break;
          }
      }
      
      // Pokračujeme až tady. Stav: $zkonceno = undefined (false), $pokracovat > 0 (true), $bid[time_end] beze změny.
      if ($zkonceno === true)
      {
          $pokracovat = false;
          continue;
      }
   }else{
            break;
   }
}
G3n3sis19
Profil
asi jste to špatně pochopili, nebo já odpovědi.

TomášK:
Pokud se tam provádí tisíc dotazů, tak je potřeba rozmyslet, jestli je to špatně navržené (a skutečně ten kód má provést tisíce dotazů) nebo jestli to není možné a pak zjistit, třeba debuggerem nebo ladícími výpisy, co ten kód opravdu dělá.
Takhle. Těch 1000 příhozů (odpovídám i na druhého odpovídajícího) se opravdu povede, ale pak se provede dotaz který mám jen pro debug ale zatím nic nedělá. No a po tom DELETE FRom .... se to sekne!



Ten kód je hodně nepřehledný, i když snaha o přehlednost se nedá upřít. Některé komentáře jsou zbytečné, např.

Chápu no.

Nemá smysl psát, co kód dělá - to je vidět, že se tam nějaký konec porovnává s time(), na to nepotřebuju komentář, zajímá mě, *proč* se to děje. Rovněž je dobré dodržovat další pravidla - např. maximální odsazení bude 4x-5x (deset je určitě moc). Pokud je potřeba víc, pak to zřejmě jde rozdělit do více funkcí a bude to čitelnější.
proč -> představ si, že aukce má konec 30 sekund a ve 20:00 má zkončit. Ale mezitím všichni odejdou a aukce fyzicky nezkončí. Po tom, co tam příjde někdo po 2 hodinách, je to v mínusu -7200 sekund. Očekávám že tam návštěvnost bude, takže by k těm tisíce příhozů rozhodně docházet nemělo, a kdo si nastaví 500 autobidů že?

If je zbytečný, vždy bude splněn, $pokracovat se kontroluje v podmínce while cyklu.
Vím, jenže jak mi to právě nešlapalo tak sem se bál že mi to funguje i přes to že $pokracovat je false. Navíc jsem tam měl if($pokracovat === 9999) aby jsem přeskočil část toho skriptu (vím je to kravina, měl sem použít komentář

$zkonceno se píše se s, není potřeba porovnávat explicitně s true, stačí jako o řádek výš if($skonceno). Tělo lze nahradit jedním break; Je také divné mít proměnné $skonceno a $pokracovat, zřejmě by to šlo sloučit do jedné proměnné.
říkám, dělal jsem různé obezličky protože myslím že chyba je někde v break; nebo continue;

Zkus se věnovat i čitelnosti kódu, daleko lépe se v něm budou hledat chyby, investovaný čas se vyplatí. Co je špatně netuším, možná je tam nějaké nekonečná smyčka nebo to prostě jen nestihne.
Asi se teĎ zachovám jako idiot, ale kromě těch zbytečných proměnných, mám tam něco špatně?

možná je tam nějaké nekonečná smyčka nebo to prostě jen nestihne.
zkončí to u DELETE FROM autobids......, v tý chvíli to má udělat break.


Otázka. Když mám break, ve while ve vnořeném foreach, breakne to oba? To je právě ten důvod proč jsem to zkoušel s tim $zkoncit a $pokracovat. Jo, toho pismena jsem si vůbec nevšim, díky za upozornění
G3n3sis19
Profil
Tori:
Tohle funguje jinak, než jak píšete v komentáři (ř.33):
if ($automat['max_bids'] != 1) //A POKUD MA JESTE NEJAKY AUTOBIDY, POKRACUJEM!
>
Pokud uživateli zbývá jen jedno automat.přihození, tak o řádek výš z něj uděláte 0 a tahle podmínka projde, přestože uživatel už nemůže přihazovat. Spíš bych dala: if max_bids > 0.
tak to není. máme 3, řádek 32 to změní na 2, projde to, změni se to na jedna, projde a smaže to, protože to máme až na lince 90. Mohu být vedle, ale počet příhozů oba dodrží. (oba mají 500 max, a přihodí se jich 1000)

Mám pocit, že příčinou překročení čas.limitu je nekonečná smyčka díky chybě logiky:
Předpokládejme situaci, že v tabulce autobid je pouze jeden účastník aukce, který je zároveň posledním přihazujícím ($bid[last_bidder]).
>
k tvému komentu
// K tomuto tedy nikdy nedojde, čas se nemění

to se ani nemá měnit. Máme hráče, na lince 14 tvého výpisu zkontroluje, jestliže už byl zkontrolován, a pokud jo tak breakne a tím vyhraje. Poté tam radsi jeste zkoncim ten while abych si byl jistej že tam nejde k cyklu
G3n3sis19
Profil
linka 93 v prvním výpisu, tam někde je chyba.
když místo break; dám continue;, nejde to. Když tam dám return $bid, funguje to úžasně
TomášK
Profil
G3n3sis19:
proč -> představ si, že aukce má konec 30 sekund a ve 20:00 má zkončit...
A přesně tohle má být v komentáři :-)

Asi se teĎ zachovám jako idiot, ale kromě těch zbytečných proměnných, mám tam něco špatně?
Není to špatně z hlediska funkčnosti, ale nepřehledně, nečitelně. Co se dá zlepšit?

** rozdělit to do více funkcí. Např. dotaz
.
                        $this->query("
                        INSERT INTO bid (
                        `id` ,
                        `product_id` ,
                        `bidder_name` ,
                        `bidder_id` ,
                        `bid_time`
                        )
                        VALUES (
                        '', '".$bid['id']."', '".$automat['username']."', '".$automat['uid']."', '".time()."'
                        );");

se v kódu opakuje, je tedy určitě vhodný kandidát na samostatnou funkci.

function insertBid($bidId, $bidder) 
{
                        $this->query("
                        INSERT INTO bid (
                        `id` ,
                        `product_id` ,
                        `bidder_name` ,
                        `bidder_id` ,
                        `bid_time`
                        )
                        VALUES (
                        '', '".$bidId."', '".$bidder['username']."', '".$bidder['uid']."', '".time()."'
                        );");
}

Název funkce i proměnných určitě jde zvolit lepší, netuším, co to vlastně dělá. I další místa jsou dobré kandidáty na vlastní funkce, i když jsou v kódu jenom jednou

** zbavit se zbytečných if/else
         $zkonceno = true;
         break;
         ...
         if($zkonceno) {...} // sem to nikdy nedojde, můžeme to tedy vyhodit


** inicializovat proměmmné - často spoléháš na výchozí hodnoty, což sice není špatně, ale přehledné také ne
** používat buď české, nebo anglické názvy proměnných
** nepoužívat číselné konstanty v kódu (10, 30, 0.1 apod.) -> vytvořit pro ně pojmenované konstanty např. MIN_TIME_INTERVAL
** používat výstižné názvy proměnných:
* "$pokracovat // $POKRACOVAT MA V SOBE MAX POCET PRIHOZU -> proměnná pokračovat se má jmenovat max_pocet_prihozu. V proměnné pokračovat očekávám boolean (true/false), ne číslo
** pro ladění se podívej na funkci assert - poslouží místo vkládání ifů, bude to stále přehledné.

zkončí to u DELETE FROM autobids......, v tý chvíli to má udělat break.
Skončí to na max_execution_time, tedy někde v cyklu obsahujícím ten delete. Break se neprovede, protože skriptu vypršel čas.

Otázka. Když mám break, ve while ve vnořeném foreach, breakne to oba?
Ne, break; a continue; ovlivní jen nejbližší cyklus. Pro vyskočení ze dvou cyklů to jde zabalit do funkcí, případně použít goto (což je téměř jediné smysluplné použití goto)
TomasJ
Profil
G3n3sis19:
Tak pokud to pak funguje správně, dej return true;
G3n3sis19
Profil
TomášK:
Ne, break; a continue; ovlivní jen nejbližší cyklus. Pro vyskočení ze dvou cyklů to jde zabalit do funkcí, případně použít goto (což je téměř jediné smysluplné použití goto)
takže tohle je špatně?:
while(true)
{
   foreach($neco as $neco2)
   {
     break;
     $brejkni = true;
   }
   if ($brejkni)
   {
     break;
   }
}
?

mám to teď tedy takto
	function proc_user_autobid($bid)
	{
		$bids_before_visiting_needed_to_extend_product = (time()-$bid['time_end'])/10;
		$automats = $this->query("SELECT * FROM autobid WHERE product_id = ".$bid['id'].";");
		if ($this->count($automats))
		{
			$bids = 0;
			while($automat = $this->fetch($automats))		//PRVNI PRUBEH
			{
				$bids++;
				$this->query("
				INSERT INTO bid (
				`id` ,
				`product_id` ,
				`bidder_name` ,
				`bidder_id` ,
				`bid_time`
				)
				VALUES (
				'', '".$bid['id']."', '".$automat['username']."', '".$automat['uid']."', '".time()."'
				);");
				$bidder_username = $automat['username'];
				$bidder_id = $automat['uid'];
				if ($automat['max_bids'] == 1)
				{
					$this->query("DELETE FROM autobid WHERE uid = '".$automat['uid']."' and product_id = '".$automat['product_id']."'");
				}
				$bid['last_bidder'] = $automat['username'];
				$bid['time_end'] += 10;
				$bid['price_now'] += 0.1*$bids;
				$automat['max_bids'] -= 1; //ZMENSIME POCET MAX. BIDU o 1
				if ($automat['max_bids'] != 1) //A POKUD MA JESTE NEJAKY AUTOBIDY, POKRACUJEM!
				{
					$pokracovat += $automat['max_bids']; //MUZEME POKRACOVAT, $POKRACOVAT MA V SOBE MAX POCET PRIHOZU
					$hraci[] = array("uid" => $automat['uid'], "bids" => $automat['max_bids'], "username" => $automat['username']);
				}
			}
			
			$preskoceno = array( );
			if ($bid['time_end']<time() and $pokracovat) //A POKUD JE KONEC MENSI NEZ TIME() 
			{
				while($bid['time_end']<time() and $pokracovat or !$vyherce) //TAK POKRACUJEM DOKUD MAME BIDY NEBO DOKUD SE KONEC AUKCE NEDOSTANE NAD NULU
				{
//					unset($preskoceno);
				//	echo memory_get_peak_usage();die();
					$preskoceno = array();
					foreach($hraci as $id => $co) //TEN CO MU JESTE ZBYVA ZKONTROLUJEME, JESTLI NENI POSLEDNI
					{
						if ($bid['last_bidder'] == $hraci[$id]['username']) //PRESNE TADY
						{
						//	if (!empty($preskoceno))
						//	{echo "asdasd";	print_r($hraci[$id]['username']);die();}
						//	die($bid['last_bidder']."-vs-".$hraci[$id]['username']);
							if (in_array($hraci[$id]['username'], $preskoceno))
							{
								$vyherce = true;
								return $bid;
							}
							else
							{
								$preskoceno[] = $hraci[$id]['username'];
								//A POKUD JE POSLEDNI, POKACUJEME K DRUHEMU
							}
						}
    						else
						{
							$bids++;
							$this->query("
								INSERT INTO bid (
							`id` ,
							`product_id` ,
							`bidder_name` ,
							`bidder_id` ,
							`bid_time`
							)
							VALUES (
							'', '".$bid['id']."', '".$hraci[$id]['username']."', '".$hraci[$id]['uid']."', '".time()."'
							);");
							$bidder_username = $hraci[$id]['username'];
							$bidder_id = $hraci[$id]['uid'];
							$bid['last_bidder'] = $hraci[$id]['username'];
							$bid['time_end'] += 30;
							$bid['price_now'] += 0.1*$bids;
							if ($hraci[$id]['bids'] == 1)
							{
								$this->query("DELETE FROM autobid WHERE uid = ".$hraci[$id]['uid']." and product_id = '".$bid['product_id']."'");
								unset($hraci[$id]);
								$this->query("UPDATE product SET 
								last_bidder = '".$bidder_username."', 
								last_bidder_id = '".$bidder_id."', 
								time_end = ".$bid['time_end'].", 
								price_now = ".$bid['price_now']." 
								WHERE id = ".$bid['id']);
								return $bid;
	
							}else{
								$hraci[$id]['bids'] -= 1; //ZMENSIME POCET MAX. BIDU o 1
							}
						}
					}
				}
			}
		
			$this->query("UPDATE product SET 
			last_bidder = '".$bidder_username."', 
			last_bidder_id = '".$bidder_id."', 
			time_end = ".$bid['time_end'].", 
			price_now = ".$bid['price_now']." 
			WHERE id = ".$bid['id']);
		}
		
		return $bid;
	}

při 2 přihazujících, oba s max. 500 bidy to funguje, u jednoho autobiddera ne. Zas max_exec_time
TomášK
Profil
G3n3sis19:
takže tohle je špatně?:
Je to funkční verze. Ale předkládám dvě ekvivalentní, které pokládám za čitelnější:

while(true)
{
   foreach($neco as $neco2)
   {
        if(...) {
            goto: done
        }
   }
}

done:
...



function do_stuff() {
   foreach($neco as $neco2)
   {
        if(...) {
            return false;
        }
   }

    return true;
}

while(true)
{
    if(!do_stuff()) {
        break;
    }
}
G3n3sis19
Profil
stále ale nevím, jak to vyřešit s tím timeoutem. Když jsou tam 2 se stejným počtem bidů, funguje to ale když je uživatel sám, máme tu timeout
G3n3sis19
Profil
fajn. dal jsem lehkou kontrolu před druhý běh. Jestliže (count($uzivatele) == 1) {return $bid;} a je to ! :)
díky moc za rady

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: