Cum stochez evenimente recurente în DB? (și cum le interoghez după)

Eu am înțeles procesul pentru un eveniment singular.

Ce nu înțeleg eu [1] este cum iau și sortez evenimentele în funcție de dată. I.e. cum arăt evenimentele de săptămâna asta? (presupun că va ajuta materializarea de care zice @tekkie mai sus?)


  1. sunt perfect conștient că neînțelegerea mea este dată de lacune în zona DB, deci fiți blânzi :smiley: ↩︎

Am lucrat mai demult pe recurente folosind vcalendar si icalendar si lucrurile se complicau cand era vorba de excluderi si exceptii. Excluderile erau ocurentele din evenimentul recurent la care ai modificat ceva. De exemplu modifici titlul sau durata pentru un eveniment din recurenta.
Exceptiile cred ca erau atunci cand stergeai un eveniment din recurenta: 3.8.5.1. Exception Date-Times | iCalendar (RFC 5545) | RFC Specifications

In unele sisteme (nu toate), excluderile ramaneau referentiate de evenimentul recurent mare. Daca stergeai evenimentul parinte, se stergeau si excluderile :slight_smile:

Eu cred ca recurenta necesita o intelegere profunda de la inceput si eventual sa fie modelata separat in DB.

1 Like

oh dear. zi-le ca research-ul a fost gratis si ca implementarea dureaza x e fapt.

Query simplu si rapid, ordonat dupa data poti face doar daca sunt si generate. De ce te sperie generarea? Chiar si cu update-urile aferente la schimbari, te ajuta muuult mai mult la actiunile cele mai frecvente: vizualizarea, gasirea unu eveniment, tratarea lui.

Pentru side-project-ul la care mesteresc am avut de’a face destul de mult cu task-uri repetitive.

Step 1 cred ca este sa revizitati domain modelling-ul o idee. Recurring events are hard. Nici macar gCal sau alte produse mari nu le au corect implementate in toate situatiile. Mi s-a intamplat sa busesc lucruri pe acolo incat delete + recreate era singura solutie. Poate are rost o reprezentare explicita a event-urilor recurente (gen le creezi in calendar, si iti apar si intr-un tab separat al aplicatiei), sau sa limitezi agresiv ce gen de recurenta poti avea (dupa care sa iterezi pe reguli pe masura ce intelegeti mai bine use-case-urile) sau sa fi foarte explicit in a recunoaste timpul (de exemplu ca un eveniment care s-a intamplat nu ar mai trebui modificat → daca vezi calendarul ca o treaba pur CRUD asta nu ar decurge). Asta am facut la side-project si alaturi de alte constrangeri ale domeniului cred ca a iesit ceva destul de “normal” si OK de inteles. Pentru mine cel putin.

Step 2 legat de reprezentarea duala e valida. Trebuie sa ai un tabel/storage pentru event-uri si alt tabel pentru event-urile recurente ca sa iti fie usor. Merg toate bagate la un loc dar pregateste-te sa devi expert in SQL the hard way :smiley: Si un proces care materializeaza event-uri in primul dintr-al doilea. In cazul side-project-ului meu e chiar o comanda pe care o ruleze de mana in fiecare zi - pana inteleg bine ce patternuri merg si ce nu, dupa care o automatizez.

2 Likes

de acord cu principiul, insa practica mi-a aratat de mai multe ori ca un cache in db poate fi solutia care bifeaza cele mai multe puncte (performanta, timp, cost, etc).

timp si cost partial de acord

nu am cum fi de acord la partea de performanta, un cache in db rchivaleaza cu traversarea intregului stack (plus zeci de proiecte mari si medii livrate in ultimii 20 ani)

1 Like

Uite cine știe ce înseamnă să lucrezi în realitate, nu în ideal :slight_smile:

Te-ai gândit să vinzi niște cursuri de programare? :))

Recent am implantat și eu pe erp-ul le care-l fac pentru firmă strategii automate de arhivare/eviction și n-a fost pentru că multe date sunt dependente evident. Am reușit să le grupez pe ani (pot avea câteva milioane de records și peste 20-30 gb pentru un singur an, si deja am 5 ani). Încă nu e complet gata dar părțile cele mai importante funcționează și din interfață cât si din api-uri se poate specifica anul în care să se caute (default e anul curent).

E greu spre imposibil să găsești oameni cu experiență reală care să știe explica și părțile astea. E plin internetul de hello world-uri reciclate și răs-reciclate dar să construiesti ceva solid long time si patternurile arhitecturale necesare pentru asta aproape nicăieri.

Doar prin cărți și experiența proprie + experiența altora.

4 Likes

Revin asupra ideii de aici (cu mai multe detalii despre usecase):

Toată hardughia este o instanță WordPress, iar evenimentele sunt stocate ca un custom post type (event). Datele despre evenimente sunt stocate în meta data, lucru care face toată situația de o încetineală aproape haioasă[1]. În total sunt în jur de 1000 evenimente (trecute și viitoare), fiecare având diferite variații/recurențe (cel mai cel eveniment are vreo 150 variații)

Despre usecase: nu este cazul de intervale dubioase sau de reguli complexe de filtrare. Pentru afișare se folosește:

  • sortarea după data evenimentului (start/end), ori ASC (cel mai aproape în viitor primul), ori DESC
  • filtrarea pe date: ori un interval (evenimentele între Iunie-August) ori de la o dată în sus (de azi până la infinit)

NU va fi vreodată nevoie de filtrări mai complexe de genul:

da si mie un biweekly miercurea la 2pm durata 45min


În fine, ideea mea este să:

  1. fac o tabelă suplimentară în care stochez datele despre evenimente, să-i zicem event_rrule (în format rrule, că pare ce trebuie din mai multe puncte de vedere). Asta va avea coloanele: id | post_id | event_id | rrule (TEXT) | reccurrence(TINYINT) | is_past(BOOL)

    • post_id este id-ul post-ului din WP
    • event_id pentru că un eveniment poate avea mai multe repetiții, fără a fi neapărat recurent la modul previzibil (e.g. un turneu)
    • rrule va include … ei bine, RRULE, indiferent dacă evenimentul este sau nu recurent (RRULE tratează o repetiție ca și cum ar fi single event)
    • încă nu sunt sigur că este nevoie de o tabelă pentru recurență, deci o coloană cred că-i suficientă?
    • is_past pentru ca atunci când rulez cron să le sar pe alea irelevante
  2. fac încă o tabelă, să-i zicem readable_event (cred că asta ar fi materializarea de care zice @tekkie ?), ce păstrează datele „în clar” despre evenimente, doar cu coloanele de care am zis mai sus: post_id | event_id | start | end în care bag evenimentele valide.

    • adițional, rulez un cron zilnic care curăță evenimentele trecute (și le mută într-o altă tabelă, similară, poate?)
    • pentru evenimentele recurente cu dată de început dar nu și sfârșit, generez instanțe pentru următorul an (și le actualizez permanent din cron)
    • la fiecare actualizare a unui eveniment (as in WP post), actualizez datele pentru evenimentul în cauză
  3. când vreau să afișez evenimentele pe o anumită perioadă, fac un select post_id from readable_event where start > NOW(), iar aceste ID-uri le folosesc pentru a interoga post-urile din WP (cu WP_Query), filtra, ordona.

Ce probleme pot întâmpina cu abordarea asta?


Ce părere aveți acum, că știți și mai multe detalii despre situație?


Nu știu curs, dar la o carte…


  1. motiv pentru care, așa cum am spus mai sus, query-urile erau făcute la modul „luăm tot din db, filtrăm din PHP” ↩︎

2 Likes
  • eu aș adăuga coloanele start și stop (DATE) la prima tabelă și ai scăpa de is_past (sau ai astea definite într-o tabelă event poate?!)
  • a 2-a tabelă aș redenumi-o în materialized_event

Cînd modifici evenimentele recurent, eu aș sterge toate intrările aferente din materialized_event și le-aș genera din nou… cel mai simplu (poate poți folosi ceva asincron, dar nu e musai)

E nevoie să anulezi evenimente individuale (din materialized_event)? (Dacă ceva cade în ziua de Craciun, probabil vrei să fie anulată…) Atunci mai adaugi o coloană aici (call it skip or canceled etc)

2 Likes

cateva idei rapide:

nu imi e clar daca e cazul aici, dar eu as fi considerat doua entitati diferite: eveniment si aparitii.
evenimentul tine toate detaliile generale (continutul lui), iar aparitiile tin tot ce e legat de “materializarea” lui.
e greu sa ai un status al evenimentului in care sa spui ca a fost “anulat” cand el are mai multe aparitii si unele sunt deja incheiate, unele sunt in desfasurare, unele au inscrierile deschise, iar altele pot fi anulate.
deci “materializarea” nu e numai despre data, e pur si simplu o entitate diferita dpmdv.
lucrul asta ar trebui sa rezolve singur mare parte din problema filtrarii.

asta cu atat mai mult poate da greutate ideii mele de cacheing in db.
conform principiului ca timpul de procesare se aloca spre system / admin si nu spre user… faci pregatirea / preprocesarea datelor la introducerea lor (sau imediat dupa prin cron, de fapt).
daca interfata aplicatiei permite selectarea perioadei de tipul: data (zi), sapt curenta, luna curenta, etc… cred ca poti pregati niste date (lucru pe care acum il faci in php la user) si sa le versi in niste tabele… pe care sa le golesti cand adaugi / editezi un eveniment care le afecteaza.

de acord, dar un cache de tipul asta inseamna:

  • capacitate mica a datelor stocate acolo (nu poti sa versi 150000 de evenimente cu detalii complete acolo)
  • un sistem in plus de “intretinut”
  • un skill in plus (sau o persoana in plus) atat la implementare… dar mai ales la mentenanta
  • un risc in plus (un exploit, o trecere pe linie moarta a tehnologiei, etc)

pentru un proiect mare facut cu o agentie… lucrul asta poate fi inghitit in cele mai multe cazuri,
dar pentru un proiect lucrat cu freelanceri… chiar paote fi o piedica (caz in care e mai performant un cache in db decat unul absent).

altfel, poate nu am ales bine termenul de cache (desi in folosea si drupal intr-o perioada pentru tabelele lui in db).
eu propuneam de fapt o preprocesare a datelor / o pregatire a lor pentru cele mai folosite situatii (gen lista de evenimente din luna x).
la o adaugare sau modificare a unui eveniment se poate decide simplu care dintre afisari sunt afectate si se golesc / regenereaza tabelele respective.

eu am invatat si pastrat tipul asta de abordare la unul dintre primele mele proiecte livrate in regim de freelancing care era un fel de protal de stiri.
lucurile au fost foarte bune la inceput… pana cand proiectul a adunat ceva continut.
lucrurie au fost si mai complicate cand s-a viralizat prima lor stire (nici nu mai stiu daca pe vremea aia se numea viralizare).
da, un cacheing in db (de tipul celui descris mai sus) a rezolvat situatia pe termen destul de lung.

poate sunt eu un dinozaur, dar chiar nu cred ca in practica toate proiectele se implementeaza conform ghidului celor mai bune practici.

ps. rog a nu se intelege ca sunt impotriva ideilor propuse de tine sau ca sustin improvizatiile in locul solutiilor stabile.

Nu există evenimente anulate. Dacă există, înseamnă că-i valid. Dacă e anulat, e șters. (i.e. nu se păstrează o arhivă cu toate evenimentele, ci doar cu cele valide).

prin anulare ma refeream la o aparitie /materializare a lui.
cineva dadea aici un exemplu cu ziua de craciun care poate fi sarita din recurenta, dar pastrate alte evenimente.
cum redefinesti recurenta evenimentului pentru a sari peste anumite aparitii care se anuleaza din orice motiv?
o sa dai in prea multe exceptii si o sa iti iasa o salata de spaghetti foarte gustoasa :slight_smile:

acum… nu stiu daca sistemul e pentru evenimete tip concert la care se vand bilete sau pentru evenimente de tipul intalniri pe zoom sau pur si simplu pentru remindere / notificari…
dar amestecarea evenimentului cu aparitiile lui in acelasi loc… mi se pare a fi punctul care te tine in problema.

Mersi @Emanuel_Gug si @iamntz de incurajari.

Uite pot sa scot de la naftalina blogul personal si sa abordez topicul de azi ca sa scot rugina, asta daca e si @iamntz de acord.

Un proiect mai vechi al meu e branchingstrategies.com, recent l-am cautat de nameservere sa il fac un github static site, ca tekkie.ro. V-ar place ideea?

5 Likes

Din fericire, e recurență simplă: la X zile/săptămâni/luni/ani și atât. Excepțiile (evenimente anulate/amânate) se tratează manual (i.e. un text :smiley: )

+1 reader. Shareaza tot ce ai

1 Like

As vrea doar sa zic ca Outlook face varianta aia brute force adică generează toate evenimentele recurente și gata. Sunt legate între ele și poți sa le dai mass update dacă vrei.
Si dacă cineva știe chestii cu calendar eu zic ca e outlook :grin:

1 Like

De-aia consumă așa multe resurse outlook-ul deci, atât pe disk cât și rami? :slight_smile:

Normal că nu-i interesează pentru că ram-ul și hdd-ul clientului, dar când faci aplicații pe cloud trebuie să fii mai inventiv :slight_smile:

Outlook ul rulează acum în cloud, ce vezi tu local e doar un view.

1 Like

Outlook-ul este doar o aplicatie de React care incarca datele de pe backend si le afiseaza local mai precis. (la fel ca tot Office 365-ul)