Autor Zpráva
zrnecx
Profil
Dobrej den,

dělám hru na telefony v Cordove, a jako server pouzivam PHP....

jsou nektere kroky ktere je potreba udelat jen jednou (Matchmaking, Priprava hry) a pri cekani na hru se odesila opakovane pozadavek jestli je hra pripravena....

nevim jak zacit, proste kdyz je request tak sem to vymyslel tak ze v nejake slozce "temp" si vytvarim indikacni soubory...

funguje to tak ze prvni pozadavek vytvori soubor, probehne priprava a pak se soubor smaze. kazdy dalsi uzivatel ktery posle pozadavek na zjisteni stavu zjisti ze soubor existuje a jen vrati vysledek true/false jestli je hra pripravena..



Pro priklad vam sem dam kod:

    private static function CanProcessMatchmaking($id)
    {
        @mkdir("temp",0777,true);
        return !file_exists("temp/".$id.".block");
    }

    private static function SignStart($id)
    {
        if(self::CanProcessMatchmaking($id))
        {
            file_put_contents("temp/".$id.".block",$id);
            return true;
        }
        else
        {
            return false;
        }

    }


    private static function SignEnd($id)
    {
        unlink("temp/".$id.".block");
    }


a kod kterym provadim akce je:

    private static function StuffToDo()
    {
        if(self::SignStart("matchmake"))
        {
            //Match Prepare Here...
            self::SignEnd("matchmake");
        }
    }

Je tohle "dobry" zpusob jak to udelat?

vim ze je pruser kdyz nabehne vyjimka a nesmaze to soubor, ale to poresim tim ze proste zadna vyjimka nemuze nastat (vse osefim)

me by jen zajmalo jestli je to spolehlivy zpusob jak udelat ze process probehne jen jednou za dobu kdy bude napr 10,1000,1000000 requestů na ten konkretni php soubor...

predem dekuji za odpovedi :)
Joker
Profil
zrnecx:
Nebylo by lepší použít databázi?
zrnecx
Profil
Joker:
tom si nejsem jisty prave.. ale asi ji pouziju no... me se nechtela kvuli tomu delat tabulka abych to jen na ryhchlo zprovoznil..

v tom pripade ta sama otazka, nemuze pri vetsim mnozstvi uzivatelu ten jedne proces probehnout 2x?

jde mi o to aby kdyz to najde 2 hrace co budo hrat proti sobe a najendou process co bezi zaraz najde jednoho z nich ale hodi to tretimu, tak by se to rozbilo, timhle sem tomu chtel zabranit.

nejlepsi by byl cron ale u wedosu muzu spustit max 1 za 5 minut coz je dlouha cekaci doba si myslim...
Joker
Profil
Tak teď nevím přesně jaký problém řešíte.

Ale jestli jde o problém paralelismu, tj.: Dvě spuštěná hledání najdou paralelně téhož volného hráče a přidělí ho do dvou různých her, tak to je řešitelné např. pomocí zámků, kdy samotnou operaci přidělení může současně dělat vždycky jen jedna instance a ostatní musejí čekat, až ta první skončí a uvolní zámek.

Přesněji nevím jak poradit dokud nemám bližší popis problému.
zrnecx
Profil
Vsak o tom je prvni prispevek, ten zamek mel byt soubor ve slozce temp a nebo jak si rikal v nejake tabulce v databazi bude 1 nebo 0 podle toho jestli uz nejaky probiha nebo neprobiha

cely proces je uz mam normalne funkcni, otazka tedy zni nasledovne:

Příklad 1, tak jak to má fungovat..
//ZÁMEK = 0
Request 1: checknout jestli je zámek 0 [true]
Request 1: nastavit zámek na 1 []
//ZÁMEK = 1
Request 1: delat operaci kterou potrebuju jen jednou zaraz []
Request 2: checknout jestli je zámek 0 [false]
Request 2: nemam co delat.. konec skriptu...
Request 1: operace dokoncena, zámek na nulu
//ZÁMEK = 0

Příklad 2, to co mam strach ze se stane
//ZÁMEK = 0
Request 1: checknout jestli je zámek 0 [true]
Request 2: checknout jestli je zámek 0 [true]
Request 1: nastavit zámek na 1 []
//ZÁMEK = 1
Request 2: nastavit zámek na 1 []
//ZÁMEK = 1
Request 1: delat operaci kterou potrebuju jen jednou zaraz []
Request 2: TAKTÉŽ delat operaci kterou potrebuju jen jednou zaraz []
Request 1: operace dokoncena, zámek na nulu
//ZÁMEK = 0 ;(nyni by se mohl pripojit treti uzivatel ktery uvidi ze je zamek 0 i kdyz ani druhy nedobehl (pritom nemel vubec bezet))
Request 2: operace dokoncena, zámek na nulu
//ZÁMEK = 0 

chápeme se? nevim jak vic polopaticky vysvetlit svuj dotaz :)
Kajman
Profil
V databázi (innodb) to vyřeší použití transakce.
zrnecx
Profil
Na wedosu:
Verze MySQL: 5.5.5-10.1.19-MariaDB

i tady?


Jinak rad bych se toho dozvedel vice.. pouzivam nette/database (momentalne v2.4.6, ale muzu pres composer normalne updatnout)
Jde to přes to nejak normalne?
T-fon
Profil
Nette má transakce jednoduše vyřešené.
zrnecx
Profil
Takže když udělám

$database->beginTransaction(); 
$info = $database->fetch("SELECT locked...."); 
if($info!==false&&$info["locked"]==0)
      $database->query("UPDATE locked na 1"); 
$database->commit(); 

a mezitím když nekdo zavola druhej pokus, tak se druhá instance sekne na begin transaction dokud v první nedám commit?

nebo to chápu úplně špatně?
Kajman
Profil
Mysql v innodb tuším zamyká záznamy na úrovni řádků, takže jiná transakce dělající select a update na jiném řádku tabulky by neměla být s touto v kolizi.

Hlavně tu tabulku vytvořte typu innodb a nevytvářejte ji jako myisam, abyste transakce nad ní mohl korektně používat.
Joker
Profil
zrnecx:
Pokud používáte databázi, není důvod si dělat zámky manuálně.

Čili postup v databázi by byl:
1. LOCK TABLES
2. UPDATE
3. UNLOCK TABLES

Možná mezi 1. a 2. ještě bude zjištění, koho vlastně aktualizovat, ale podle mě by to šlo udělat jedním dotazem.
Něco na způsob:
UPDATE players SET idGame=id aktuální hry WHERE hráč ještě nemá přidělenou hru LIMIT 1

Pokud se spustí víc instancí, tak všechny ostatní budou čekat na LOCK TABLES dokud se zámek neuvolní.
Kajman
Profil
Lock tables je postup pro starší myisam tabulky.
zrnecx
Profil
já tomu teda nerozumím... (mozna je to tim ze piju tu dedovu slivovicu) jak by teda vypadal kod, ktery pouziva nette database......


zadani vypada tak, ze mam JSON api... aplikace se kni pripoji pomoci tokenu ktry dostane pri prihlaseni

tabulka vypada takhle:
Tabulka: match_making

id    int(11) Auto Increment    //AI 
team    mediumtext                    //JSON členove teamu
game_type    int(11)            // typ hry
matched    tinyint(4)                    //bool 1/0 (uz byl prirazen ke hre? SQL: "WHERE matched = 0")
match_room    int(11)              // IDcko hry do ktere byl pridan, pokud je matched 1

druha tabulka s hrama:

Tabulka: match_room
id    int(11) Auto Increment    //IDcko hry viz "match_room" v match_making
game_type    int(11)            //typ hry jako v predchozi tabulce
players_team1    mediumtext    //seznm rhacu tymu 1
players_team2    mediumtext    //seznam hracu tymu 2
json_game_stack    longtext    //trida hry ulozena pomoci funkce "serialize()" (jo chtel sme json ale to neulozi celou tridu)
finished    tinyint(4)                    //1/0 true false
ready    tinyint(4)                    //1/0 true false


v klientovi se pripojuje na api kazdych nekolik stovek ms kazdy klient co hleda mathmaking, a kazdy z nich ktery objevi ze nikdo nedela matchmaking ho bude delat...


pseudokod toho co chci:
NetteDatabase: Check jestli je lock 0
NetteDatabase: Nastaveni locku na 1 a okamzite zablokovani moznosti upravy tabulky jinym procesem 
NetteDatabase: zkusit jestli neni tabulka zamknuta (transakce?) a nastavit lock na 1
Ostatni kod: najit pár a uložit ho do match_room a v match_making nastavit matched na 1 (to mam nakodovane, tenhle muzete vlozit jen jako komentar "do"
NetteDatabase: Hodit lock na 0 a umoznit jinnym procesum hledat match

Pardon ze se ptam jak retard, ale fakt nevim jak ty transakce fungují, kdyby jste mi sem napsali nejaky ten kod toho zamykani byl bych nadmíru štasten..

dekuji pánové za rady :) :* mam vas rad :)
Kcko
Profil
zrnecx:
Jak se pracuje s Nette database najdeš na Nette.org, to bys mohl vědět, když Nette používáš viz doc.nette.org/cs/2.4/database-core (dole).
Nastuduj si to trošku , napiš nějaký kód a pak se ptej dál (a to spíš na Nette fóru), tahle diskuse není Nette diskuse.
zrnecx
Profil
Tak nette nepouzivam, jenom tu databazi a tracy, jinak to nema s nette nic spolecneho... dik za odpovedi :)
T-fon
Profil
Pokud používáš Nette Database, tak to můžeš řešit těma transakcema, na který jsem ti už posílal odkaz. Asi to teda neni nejideálnější řešení, ale funguje to a je to jednoduchý na použití.
Kcko
Profil
zrnecx:
Mno a? Já taky na svém projektu, který není postaven na Nette používám moduly z Nette (Tracy, Nette database, Forms, Latte). S Nette database se pracuje pořád stejně.
Nemáš zač, když se budeš trošku snažit, tak to napíšeš.


Tady máš příkazy jak pracovat s transakcí. Zahájíš / Potvrdíš nebo Zrušíš.

Joker
Profil
zrnecx:
Dotaz k návrhu té databáze:
Skutečně obsahuje sloupec matched v tabulce match_making nějakou novou informaci?

Není to tak, že v matched je 1 právě tehdy když v match_room je ID hry a 0 právě tehdy když match_room je NULL (popř. 0)?

Podle mě by to snad ani speciální zámek nevyžadovalo.
Když by se hra přidělovala dotazem na způsob:
UPDATE match_making SET match_room = ID hry WHERE match_room IS NULL [AND případné další podmínky] [ORDER BY nějaká priorita přidělování] LIMIT 1

…tak by si databáze měla sama ohlídat, že to nechytí dvakrát stejný řádek.
zrnecx
Profil
tak vono je to tam kvuli historii kdo byl skym matchnuty abych mohl debugovat, ale tohle vypada hezky
Joker
Profil
zrnecx:
Jenže to může uchovat historii stejně jen o krok zpátky (tj. když bude v matched 0, může v match_room být ID předchozí hry).

Jestli chcete historii, udělal bych pro každou hru nový záznam.
Aktuálně to předpokládám funguje tak, že přidělení hry nastaví matched=1 a ID hry, následně po hře se přenastaví matched=0 (a zůstane staré ID hry) a další přidělení přepíše ID hry a nastaví zase matched=1.

Pro zachování historie by šlo dát každému záznamu ještě stav, po skončení hry by se záznam nastavil jako už neaktivní a před další hrou by se založil nový záznam.
zrnecx
Profil
Joker:
Jenže to může uchovat historii stejně jen o krok zpátky

nene, uchova ji celou protože

Aktuálně to předpokládám...
... špatně.


Takhle vypada tabulka (nikdo zrovna hru nehledá)


Takhle vypadá tabulka her:


Prostě historii to uloží... neměl sem ted 2 dny cas na tom delat... jestli chcete, tak dam vedet jak dopadla ta moje zkouska s transakcema...
taky sem jeste narazil na nette/safe-stream.. to by melo jit pouzit kdyz dam na zacatku fopen a po dokonceni fclose...

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

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

0