[PHP] Eigen sessie systeem thread safe maken

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Hipska
  • Registratie: Mei 2008
  • Laatst online: 08-09 09:58
Ik heb in php een eigen sessie systeem gemaakt omdat het standaard systeem niet zo heel erg goed met objecten in combinatie met autoloading overweg kan en ook omdat ik meer op een OOP manier met sessie waarden wil werken.

Ik schrijf de gegevens weg in een file dmv var_export en haal ze de volgende keer weer op met include als hieronder beschreven. de save methode wordt door de destructor uitgevoerd.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?
    public function load(){
        if(file_exists($this->fileName)){
            
            //TODO: insert thread safety
            $this->data = include $this->fileName;


            if(is_array($this->data)) return true;
            else{
                $this->data = array();
                return false;
            }
        }else return false;
    }

    public function save(){

        if(!file_exists($this->fileName)){
            // create file
            // chmod to read/write
            // throw exception
        }
        
        $data = '<?php'.PHP_EOL.'return '.var_export($this->data,true).';'.PHP_EOL.'?>';
        //TODO: insert thread safety by creating lock file
        return file_put_contents($this->fileName, $data);
    }
?>


Nu heb ik ondervonden dat wanneer ik de pagina te snel herlaad, dat het object nog niet de nieuwste gegevens bevat, maar nog de gegevens van de request van daar voor. Dit heb ik getest door de microtime te setten tijdens elke page refresh.

Nu ben ik beginnen uitzoeken hoe ik dit probleem kan oplossen en heb 1 mogelijke oplossing gevonden, namelijk: een lock op de file zetten.

Hiervoor heb ik 2 uitwerkingen:
Bij beide zou ik voor het lezen en voor het schrijven een lock van de file eisen.
1: Zelf een .lock bestand plaatsen en controleren of dit bestaat.
2: via de flock functie

Maar nu is mijn vraag, is dit wel een goede oplossing?
Indien ja: Welke van de 2 is dan te verkiezen?
Indien nee: Is er een andere manier om dit probleem te verhelpen?
Alsook: Waardoor komt het nou eigenlijk dat dit voorkomt bij mij en niet met $_SESSION? Duurt var_export veel langer dan serialize?

Tijdens het typen van deze post ben ik gestoten op unserialize_callback_func. Kan dit werken met sessions als ik een autoloader daarin definieer?

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02:32
flock() op het sessiebestand zelf lijkt me een prima oplossing. Waarom zou je een extra lockfile introduceren met de daarbij behorende overhead als je al een enkel bestand hebt om op te locken?
Waardoor komt het nou eigenlijk dat dit voorkomt bij mij en niet met $_SESSION? Duurt var_export veel langer dan serialize?
Ik neem aan dat PHP's sessie management ook aan locking doet, ofwel met filesystem locks (flock) ofwel intern (wat misschien minder praktisch is als er meerdere PHP processen draaien, hoewel je dan nog shared memory zou kunnen gebruiken). Je kunt immers altijd het probleem hebben dat een sessiebestand pas voor de helft geschreven is als je 'm voor de volgende request weer opent.

[ Voor 60% gewijzigd door Soultaker op 22-04-2010 22:27 ]


Acties:
  • 0 Henk 'm!

  • !null
  • Registratie: Maart 2008
  • Laatst online: 11-09 14:00
Misschien mis ik iets, maar waarom gebruik je geen database? Heb je meteen een atomaire manier van je sessie informatie opslaan.

[ Voor 7% gewijzigd door !null op 22-04-2010 22:28 ]

Ampera-e (60kWh) -> (66kWh)


Acties:
  • 0 Henk 'm!

  • Hipska
  • Registratie: Mei 2008
  • Laatst online: 08-09 09:58
Waarom zou ik wel een database moeten gebruiken?
+ dat neemt het feit niet weg dat ik bij snelle refreshes nog met de oude data zou zitten.
En daarbij zijn INSERT's of UPDATE's toch behoorlijk 'duur'?

@soultaker: Bedankt! flock is volgens mij toch de weg die ik ga inslaan om de problemen te verhelpen.

Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 19:03

Sebazzz

3dp

DELETE's zijn duur, maar UPDATE's en INSERT's zijn niet bijzonder prijzing. Wel aangenomen dat je een efficient datamodel hebt genomen. Als je niet van variabele kolommen gebruik maakt (geen var*).
Opzoeken is ook niet duur, als je indexes op de juiste kolommen zet.

[ Voor 21% gewijzigd door Sebazzz op 23-04-2010 00:08 ]

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Hipska schreef op donderdag 22 april 2010 @ 23:43:
Waarom zou ik wel een database moeten gebruiken?
Wat denk je dat er gaat gebeuren als je geen database gebruikt, wel sessies moet gebruiken, en in een applicatieomgeving werkt waarbij er meerdere webservers zijn waarover geloadbalanced wordt?

Dit nog ff afgezien van 't feit dat je met jouw redenatie voor geen enkele vorm van dataopslag databases nodig hebt :)

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Of andere redenatie: als je zo bij het filesystem blijft, kan je net zo goed PHP's eigen stomme implementatie gebruiken. :P

{signature}


Acties:
  • 0 Henk 'm!

  • frickY
  • Registratie: Juli 2001
  • Laatst online: 11-09 13:55
De interne session-handler die met files werkt locked de session-file ook.
De variabelen van het eerste request moeten eerst worden weggeschreven voordat een volgende request die kan gebruiken. Dat wegschrijven gebeurd automatisch aan het einde van de script executie, of door het callen van session_write_close() (wijzigigen aan $_SESSION worden daarna dus niet meer opgeslagen).
Door dit locken kun je dus maar 1 thread tegelijk hebben draaien die de sessie-vars beschikbaar heeft, en dat is ook logisch.

Zo ben je overigens de interne handler aan het nabouwen, wat me enorm onverstandig (en overbodig) lijkt. De standaard handler kan prima met objecten en autoloading overweg, zolang je je autoloader maar registreerd voordat je de sessie start. Anders kunnen de objecten niet deserialized worden en krijg je __INCOMPLETE_OBJECT__'s.

[ Voor 30% gewijzigd door frickY op 07-05-2010 00:04 ]


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
Hydra schreef op vrijdag 23 april 2010 @ 12:08:
[...]


Wat denk je dat er gaat gebeuren als je geen database gebruikt, wel sessies moet gebruiken, en in een applicatieomgeving werkt waarbij er meerdere webservers zijn waarover geloadbalanced wordt?

Dit nog ff afgezien van 't feit dat je met jouw redenatie voor geen enkele vorm van dataopslag databases nodig hebt :)
Dan stuurt de loadbalancer dezelfde gebruiker altijd naar dezelfde webserver toe.

Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
Hydra schreef op vrijdag 23 april 2010 @ 12:08:
Wat denk je dat er gaat gebeuren als je geen database gebruikt, wel sessies moet gebruiken, en in een applicatieomgeving werkt waarbij er meerdere webservers zijn waarover geloadbalanced wordt?
Waarom denk je dat dit een requirement van de topic starter is?
Hipska schreef op donderdag 22 april 2010 @ 22:07:
Tijdens het typen van deze post ben ik gestoten op unserialize_callback_func. Kan dit werken met sessions als ik een autoloader daarin definieer?
Zou je moeten testen, maar desnoods gebruik je iets als

PHP:
1
2
3
$_SESSION['foo'] = serialize($objects);

if(isset($_SESION['foo'])) $objects = unserialize($_SESSION['foo']);

Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 18:20
!null schreef op donderdag 22 april 2010 @ 22:27:
Misschien mis ik iets, maar waarom gebruik je geen database? Heb je meteen een atomaire manier van je sessie informatie opslaan.
haha, dat is #9 van "how to suck at programming": http://www.finalint.com/2...s-to-suck-at-programming/

:P

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • eamelink
  • Registratie: Juni 2001
  • Niet online

eamelink

Droptikkels

Neehoor, daar staat dat je niet elke keer dat je een stukje informatie nodig hebt binnen één request een database aanroep moet doen. Niet dat je je sessie er niet uit mag trekken aan het begin van een request en aan het einde van de request weer terugstoppen :)

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Ehh, nee, daar staat een willekeurige andere fout met een database. :z

edit:
epic trage spuit 11

[ Voor 18% gewijzigd door Voutloos op 07-05-2010 23:06 ]

{signature}


Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 18:20
eamelink schreef op vrijdag 07 mei 2010 @ 22:59:
[...]

Neehoor, daar staat dat je niet elke keer dat je een stukje informatie nodig hebt binnen één request een database aanroep moet doen. Niet dat je je sessie er niet uit mag trekken aan het begin van een request en aan het einde van de request weer terugstoppen :)
'Any' staat wel tussen haakjes. Dus ja, elke keer binnen een request die info eruit trekken is inderdaad NOG erger ;) Maar het niet gebruiken van een session scope en daarvoor naar de DB gaan is toch ook best wel erg eigenlijk. De enige plek voor de session scope is natuurlijk gewoon RAM, en vanzelfsprekend stop je daar niet -te- veel in (overstuffed session anti-pattern).

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Standaard session storage is ook niet in ram :z

Leuk zo'n random hippe dit-is-een-top-10-dus-goed blogpost als argument (terwijl het niet eens op het punt aansluit), maar ik hoor liever concrete argumenten van je.

[ Voor 11% gewijzigd door Voutloos op 07-05-2010 23:19 ]

{signature}


Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 18:20
Voutloos schreef op vrijdag 07 mei 2010 @ 23:17:
Standaard session storage is ook niet in ram :z
Dat meen je niet? Okay, misschien kun je dan PHP op Quercus draaien? Dan kun je gewoon de 'normale' session scope gebruiken in je PHP scripts.
Leuk zo'n random hippe dit-is-een-top-10-dus-goed blogpost als argument (terwijl het niet eens op het punt aansluit), maar ik hoor liever concrete argumenten van je.
1.
Over het algemeen probeer je data access naar je DB altijd zoveel mogelijk te vermijden. Minimaliseren van het aantal read queries is iets wat je sowieso al moet doen en dat geldt dubbel voor write queries. Redelijk normaal voor een web app is een I/O read-write mix tussen de 90/10 en 80/20. Elke request een write doen, ik weet het niet.

2.
Als je systeem goed opdeelt, dan is de web layer een client van je business code, die is weer een client van je persistence code en alleen die praat met je DB. Session data is een artifact die bij je web layer hoort. Het is daar temporary (session scoped dus), local data. Als je dat in je (main) DB gaat opslaan, haal je toch wel wat laagjes door elkaar. Eventueel een local in-memory database (zoals hsqldb) zou dan nog wel kunnen, maar...

3.
Waarom zou je moeten. Gebruik gewoon een normale memory based session scope zoals die al sinds mensenheugenis bestaat :P Het zou een beetje raar zijn als PHP dat niet zou hebben, maar zoals gezegd met Quercus kun je in ieder geval wel een gewone session scope gebruiken.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • eamelink
  • Registratie: Juni 2001
  • Niet online

eamelink

Droptikkels

flowerp schreef op vrijdag 07 mei 2010 @ 23:31:
Over het algemeen probeer je data access naar je DB altijd zoveel mogelijk te vermijden. Minimaliseren van het aantal read queries is iets wat je sowieso al moet doen en dat geldt dubbel voor write queries. Redelijk normaal voor een web app is een I/O read-write mix tussen de 90/10 en 80/20. Elke request een write doen, ik weet het niet.
In negen van de tien gevallen is performance sowieso geen probleem en in het tiende geval kan je je sessions tabel gemakkelijk in een andere database op een andere machine draaien.
Als je systeem goed opdeelt, dan is de web layer een client van je business code, die is weer een client van je persistence code en alleen die praat met je DB. Session data is een artifact die bij je web layer hoort. Het is daar temporary (session scoped dus), local data. Als je dat in je (main) DB gaat opslaan, haal je toch wel wat laagjes door elkaar. Eventueel een local in-memory database (zoals hsqldb) zou dan nog wel kunnen, maar...
Een PHP session heeft over het algemeen een grotere scope dan bijvoorbeeld de session-scope van Seam. Het is niet persé heel tijdelijk en ook niet vreemd om die op disk op te slaan. Een database is dan geen gekke plek.
Waarom zou je moeten.
Natuurlijk móet het niet, maar het kán prima en er is ook niets mis mee. Begrijp me niet verkeerd, ik heb geen bezwaren tegen sessions storage in een key based storage in het geheugen, maar dat is gewoon een van de mogelijkheden. Een database is een andere, die een aantal nadelen kan hebben, maar ook een aantal voordelen.

Eén voordeel is al genoemd; als je een centrale sessie-opslag hebt, dan heb je geen session-affinity nodig binnen je netwerk en is het vaak gemakkelijker om extra frontends toe te voegen of bestaande frontends uit te schakelen zonder dat er sessies stuk gaan.

Verder kan het ook best interessant zijn om relaties te leggen tussen je sessies en je overige database data. Kijk hier bijvoorbeeld bij T.net, als je uit wilt loggen zie je een lijst met je openstaande sessies en kan je selecteren welke je wilt beeindigen. Verder kan je bijvoorbeeld gemakkelijk bijhouden hoevaak een user is ingelogd of wanneer voor het laatst. De mogelijkheden zijn eindeloos ;)

Begrijp me niet verkeerd; op de manier die jij voorstelt kan je prima een sessie-systeem bouwen. Maar dat betekent niet dat andere manieren fout zijn. Je argument lijkt nogal op het klassieke en bij IT'ers overbekende riedeltje 'Mijn manier is goed en jouw manier is anders dus jouw manier is fout' terwijl er geen enkele aanwijzing is dat er een maar één goede oplossing is voor het probleem.

Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 18:20
eamelink schreef op zaterdag 08 mei 2010 @ 00:48:
[...]
In negen van de tien gevallen is performance sowieso geen probleem en in het tiende geval kan je je sessions tabel gemakkelijk in een andere database op een andere machine draaien.
Wat ik juist zie is dat nagenoeg elke applicatie, of je nu veel users hebt die relatief weinig doen, of weinig users die relatief veel doen, performance bijna altijd wel een belangrijk punt is. Wij hebben b.v. een DB server staan met 48 SSDs op 4 Areca RAID kaarten, en nog denk je, het zou toch wel sneller kunnen ;)
Eén voordeel is al genoemd; als je een centrale sessie-opslag hebt, dan heb je geen session-affinity nodig binnen je netwerk en is het vaak gemakkelijker om extra frontends toe te voegen of bestaande frontends uit te schakelen zonder dat er sessies stuk gaan.
Klopt, dat zou een voordeel kunnen zijn, maar dan verleg je natuurlijk wel weer de load naar je (remote) DB toe, die dan een bottleneck of spof wordt. Je kunt natuurlijk dan wel weer de DB gaan clusteren, maar dan had je ook net zo goed ook de web clients en hun session data kunnen clusteren.
Verder kan je bijvoorbeeld gemakkelijk bijhouden hoevaak een user is ingelogd of wanneer voor het laatst. De mogelijkheden zijn eindeloos ;)
Dat is ook waar ;) Toch, je kunt dat ook bereiken door alleen die write te doen als de user inlogt. Dat kun je ook makkelijk asynchroon doen tjdens die ene log-in actie, want deze user hoeft niet op de results te wachten. Sessie data telkens wegschrijven kan beter niet asynchroon in verband met de volgende request die kan volgen.

Over volgende requesten gesproken, hoe doe je dat dan eigenlijk met AJAX requests? In mijn systemen heeft de handler die een AJAX request processed gewoon de toegang tot de volledige session scope (en in mijn geval ook nog de conversation scope, maar laten we dat hier even buiten houden ;)). Omdat AJAX requesten ten eerste nogal veelvuldig kunnen optreden, en ten tweede voor de gebruiker helemaal asynchroon zijn (hij kan heel makkelijk op een andere knop drukken terwijl de AJAX request nog loopt), lijkt me dit nogal rottig.

Alleen al de frequentie. Stel je hebt een progressbar die 5x per seconde polled, ga je dan telkens die DB read en write van je hele session doen?
Begrijp me niet verkeerd; op de manier die jij voorstelt kan je prima een sessie-systeem bouwen. Maar dat betekent niet dat andere manieren fout zijn. Je argument lijkt nogal op het klassieke en bij IT'ers overbekende riedeltje 'Mijn manier is goed en jouw manier is anders dus jouw manier is fout' terwijl er geen enkele aanwijzing is dat er een maar één goede oplossing is voor het probleem.
Dat is inderdaad een veelvoorkomende valstrik. Ik noemde echter wel al de lokale in memory DB, dat is een beetje een compromis. De remote (main) DB als 1 grote state machine gebruiken zonder verdere caching, ik weet het nog steeds niet. Het voelt ook een beetje aan zoals in de memory hierarchy van een enkele computer. In dit geval komt het overeen met dat data van je main memory in zeg je L3 laadt, telkens als jouw process een CPU krijgt en dat je synchroon bij een context switch de entries in je L3 weer flushed naar main memory.

Dat is natuurlijk niet wat er gebeurd. Je L3 blijft gewoon gevuld tussen context switches en alleen als de L3 geleegd wordt omdat er ruimte nodig is, moet er bij een context switch geladen worden.

Het idee van je session laten backen door een main DB kan dus inderdaad voordelen hebben, maar dan zou ik het zelf eerlijk gezegd combineren met een in memory 'gewone' session scope. Je laadt hem dan alleen is als ie nog geheel leeg is, of geflushed en je schrijft alleen als er session entries 'dirty' zijn.

Ook vraag ik me sterk af of je zoiets zelf moet gaan zitten schrijven. Ik weet niet precies wat er wel en niet beschikbaar is voor PHP, maar voor diverse andere platformen is dit al zo oud als de weg naar Rome.

Voor Java kun je b.v. bij de meeste implementaties voor de HTTP session een cache inpluggen (b.v. JBoss Cache) en die kun je gemakkelijk laten replicaten via verschillende schema's (b.v. buddy replication) en daar kun je dan ook weer je eigen persisters in zetten, die b.v. naar disk persisten of in dit geval de DB. Je kunt natuurlijk de replication naar andere nodes achterwege laten, en het alleen als locale cache laten fungeren met de DB als backend.

Aangezien dergelijke mechanismes al zeer lang bestaan in de IT in het algemeen, lijkt het me erg stug als er niet al zoiets bestaat voor PHP.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • Hipska
  • Registratie: Mei 2008
  • Laatst online: 08-09 09:58
frickY schreef op vrijdag 07 mei 2010 @ 00:02:
Zo ben je overigens de interne handler aan het nabouwen, wat me enorm onverstandig (en overbodig) lijkt. De standaard handler kan prima met objecten en autoloading overweg, zolang je je autoloader maar registreerd voordat je de sessie start. Anders kunnen de objecten niet deserialized worden en krijg je __INCOMPLETE_OBJECT__'s.
Getest, en inderdaad :D

Ik prop nu de objecten gewoon in een waarde in $_SESSION en de volgende refresh kent hij nog steeds het type..

Bedankt voor de nuttige antwoorden (daarmee bedoel ik zeker niet die nutteloze discussie over DB opslaan, want dat is voor sessions natuurlijk dikke zever ;-) )

Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Hipska schreef op zaterdag 08 mei 2010 @ 13:52:
Bedankt voor de nuttige antwoorden (daarmee bedoel ik zeker niet die nutteloze discussie over DB opslaan, want dat is voor sessions natuurlijk dikke zever ;-) )
Onzin, veel session systemen gebruiken een database om het in op te slaan :P
En filesystem zijn meestal al overloaded en niet bijzonder snel, dus dat zou niet mijn voorkeur hebben. Wat mij betreft zou je sessies het liefst gewoon in geheugen (memcached oid.) opslaan ;)

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • Hipska
  • Registratie: Mei 2008
  • Laatst online: 08-09 09:58
I know, vandaar ook die smiley.
Alternatief op die memcached kun je dus ook gewoon in een file schrijven op een ramdisk..

Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 09-09 12:00

TheNephilim

Wtfuzzle

Hier @ work gebruiken we voor een oud project nog het standaard-php-sessie-systeem. Maar omdat we met meerder (loadbalanced) webservers zitten, staan de sessies op een NFS share. Erg onhandelbaar.

Onze nieuwe projecten maken gebruik van een db (MySQL, MEMORY-table) om de sessies in op te slaan en dat bevalt prima.
Pagina: 1