Jurnal de schimbări
Ce e nou.
Fiecare îmbunătățire a platformei, pe măsură ce se livrează.
· KBService
Blocați matura comune, și rula două capsule
Fiecare @Scheduled matura aici al cărui efect este împărtășit acum ia un nume de leasing distribuit prin Singleton TaskRunner înainte de a face ceva, așa că rulează o dată peste flotă în loc de o dată pe Pod. Acesta a fost singurul lucru care a păstrat acest serviciu la o singură replică nu există nici ShedLock oriunde pe Această platformă, deci numărul replica a fost ceea ce a făcut matura corect. @Transacţional şi prietenii se menţin pe metoda CLAT în cazul în care prezent: Primăvara le aplică cu o proxy, și metoda interioară se ajunge printr-o astfel:: referință, care nu trece niciodată prin una. Mutarea lor în interior ar lăsa matura tăcut non-tranzactional. replica 1 -> 2, cu un PodDiruptionBudget în KlusterServices astfel încât un nod de scurgere așteaptă.
· ESigService
Blocați matura comune, și rula două capsule
Fiecare @Scheduled matura aici al cărui efect este împărtășit acum ia un nume de leasing distribuit prin Singleton TaskRunner înainte de a face ceva, așa că rulează o dată peste flotă în loc de o dată pe Pod. Acesta a fost singurul lucru care a păstrat acest serviciu la o singură replică nu există nici ShedLock oriunde pe Această platformă, deci numărul replica a fost ceea ce a făcut matura corect. @Transacţional şi prietenii se menţin pe metoda CLAT în cazul în care prezent: Primăvara le aplică cu o proxy, și metoda interioară se ajunge printr-o astfel:: referință, care nu trece niciodată prin una. Mutarea lor în interior ar lăsa matura tăcut non-tranzactional. replica 1 -> 2, cu un PodDiruptionBudget în KlusterServices astfel încât un nod de scurgere așteaptă.
· AIService
Blocați matura comune, și rula două capsule
Fiecare @Scheduled matura aici al cărui efect este împărtășit acum ia un nume de leasing distribuit prin Singleton TaskRunner înainte de a face ceva, așa că rulează o dată peste flotă în loc de o dată pe Pod. Acesta a fost singurul lucru care a păstrat acest serviciu la o singură replică nu există nici ShedLock oriunde pe Această platformă, deci numărul replica a fost ceea ce a făcut matura corect. @Transacţional şi prietenii se menţin pe metoda CLAT în cazul în care prezent: Primăvara le aplică cu o proxy, și metoda interioară se ajunge printr-o astfel:: referință, care nu trece niciodată prin una. Mutarea lor în interior ar lăsa matura tăcut non-tranzactional. replica 1 -> 2, cu un PodDiruptionBudget în KlusterServices astfel încât un nod de scurgere așteaptă.
· kamo-internal
sesiuni de coder lângă cochilii de rulare, și o bară de defilare care ajunge la ambele capete
## Terminals is now two views of one machine Sub-tabe, mai degrabă decât două file de nivel superior, deoarece acestea nu sunt două destinații: un terminal este un shell care poate fi difuzate o sesiune, o sesiune este o conversație care ar putea avea o coajă. Comutatorul este un control segmentat, nu un al doilea file MUI benzi Ăştia doi, care este ceea ce este. Fiecare jumătate îşi poartă propria creaţie. arătat doar în timp ce sa prăbușit, în cazul în care nici un rând se potrivește. TerminalsWorkbench este împărţit de Centrul de Interacţiuni şi maximizat navigator, exact ca terminale Pane a fost deja, astfel încât cele două nu pot pluti. Acesta ia lista terminalului ca o recuzită mai degrabă decât sondaje din nou Ţine-l deja. ## Two verbs, and which one seems IS the state O sesiune care rulează deja într-un terminal spune **Deschide** și ridică fereastra. Una care nu spune **Resume** și deschide un terminal care preia înapoi. Niciodată atât, și niciodată un rând cu handicap. Reluarea unei sesiuni care este deja Ataşat oferă o conversaţie două capete: ambele CLI scriu acelaşi transcriere și al doilea se deschide prin reluarea ceea ce primul este în mijlocul. Regula este o singură funcție, şi diferit. Aparatul lasă o chitanţă în momentul în care pregăteşte una şi spune mai degrabă decât să se ofere să reia ceva ce nu a fost scris încă. Sesiunile sunt grupate pe zi. Patru sute dintr-o coloană plată este un zid; aceeasi 400 sub astazi / Ieri / Mai devreme in aceasta saptamana / Inainte de asta E o listă pe care o poţi găsi ieri după-amiază. Lista păstrează împrejurimile norme: aceeași geometrie a rândului și aceeași șină 3px ca și lista terminalelor și aceeași scindare tipografică Sans, ceea ce maşina raportează este setat în mono emulator lui. Nimic de aici nu poate cere un mod de permisiune. Fiecare sesiune începe cu permisiuni ocolite și care este decis pe aparatul de dev; numele cererii care sesiune şi nimic altceva. Un test de pini pe care clientul nu poate crește un astfel de parametru. ## The terminal's own dellbar xterm atrage bara gri a browser-ului pe viewport-ul său, care poate oferi doar "un pic sus şi puţin mai jos. Într-un terminal, cele două capete nu mai sunt două poziții. Sunt cele două locuri pe care le vrei de fapt, începutul a ceea ce ai fugit și live prompt Salt-to-end sub săgeata în jos, cu apăsați-și-hold repeta pe săgețile, Faceți clic-la-pagină pe pista și un deget mare glisabil. propriul bar xterm este ascuns, nu Restyled; viewport încă suluri, astfel încât roata, trackpad și atingere sunt neschimbate. Luminile butonului de salt-la-sfârşit când ieşirea a ajuns sub ori. Un terminal care a încetat să mai urmeze arata exact ca unul care a terminat, și care este Singurul lucru pe care barul știe că cititorul nu poate vedea. ## Verificare Poarta completa: 14 paznici sursa si 5322 teste, plus TSC. Noul UI a fost, de asemenea, redate într-un browser dintr-un ham local și citit mai degrabă decât presupusă care prins un cip verb transparent care arată titlul trunchiat prin cuvântul "Recunoaște" și o stare goală care a strălucit peste o mașină cu 400 sesiuni, deoarece secure-core raportează zero încălcări WCAG AA pe noile componente. ă workbench are propria MotionConfig: Centrul de Interactiuni isi infasoara panoul într-un singur, navigatorul nu, și ramer-motion în afara unul animează drept prin preferate-redus-motion fără un cuvânt. Cheile de la dicţionar au aterizat mai întâi, aşa cum cere CI.
· SecurityService
Listați sesiunile de coder unui membru, și deschideți unul într-un terminal
Două obiective în afară de cele terminale, și acestea urmează aceeași regulă care fac: contul Linux este rezolvat din propria SESIUNE a CALLER și nici un nume de cont este luat vreodată din cerere. Prin urmare, un membru poate vedea sau redeschide doar propriile sesiuni nu există nici un ID ei ar putea furniza care ar ajunge la cineva Altfel, deoarece contul nu face parte din ceea ce furnizează. Mașină de dev imposibil de găsit se întoarce 200 cu lista, ca lista terminal nu: "mașina este în jos" este ceva fila poate spune, în cazul în care o eroare lasă posibilitatea de a spune nimic. sesiune nouă sau reia una existentă. Nimic nu merge când POST se întoarce. Aparatul scrie sesiunea în jos și înapoiază o chitanţă; fereastra terminală pe care consola o deschide în continuare prezintă biletul obișnuit, și chitanța este ceea ce transformă terminalul în această sesiune acelaşi mod de a trimite la AI, prin acelaşi terminal DispatchRegistry. Id fereastră este numit AICI pentru că ambele jumătăți au nevoie de ea: consola pentru a deschide fereastra, registrul pentru a tasta chitanta, si tmux pentru a supravietui reconecteaza există pentru a permite. Acest obiectiv nu poate exprima o comandă, un model, un efort sau un mod de permisiune. Fiecare dintre acestea este aparatul de a decide. Ce trece firul este care sesiune, și nimic altceva sesiunile se desfășoară cu, nu este un parametru aici și nu poate deveni unul. TerminalSummary creste pe opțiunea de utilizator tmux seturile de agent. Este ceea ce permite consola spune "deja deschis" în loc să reluăm o conversaţie care trăieşte în alta Nu există alt loc de unde ar putea veni asta. Deschiderea unuia este auditată exact ca deschiderea unui carapace este de același tip de eveniment, același Severitate mare Verificat cu testul .
· KlusterServices
Măsurați dacă o desfășurare costă efectiv pe oricine o cerere
Fiecare fix în acest program este o afirmație despre ceea ce se întâmplă în timpul unui rollout. Până acum acele cereri au fost netestate: MediaService expediate cu strategie Recrea la o replica pentru toata viata sa, asa ca fiecare instalare a șters singurul pod servind chat și atașamente și a început înlocuirea după aceea o a doua gaură ~65, de mai multe ori pe zi și nimic nu a măsurat-o. Primul cineva Ştiam că un membru raportează un document eşuat, şi chiar şi asta a fost citit ca o caracteristică nefondată. decât ca o discuţie să fie pe un program. Un exportator de cutii negre încarcă acum cele opt gazde publice la fiecare 10 secunde. Zece, nu 30 de ani implicit: eșecul acest lucru există pentru a prinde este scurt de proiectare, și la 30 de ani un grafic curat ar însemna doar Gaura a căzut între două zgârieturi. Două alerte contează, iar a doua este mai utilă: PublicHostDown o gazdă nu a răspuns pentru un minut. O pană de curent sau nu. PublicHostFlapping sub 100% succes peste zece minute. Acesta este cel care prinde o lansare dropping o mână de cereri Care este exact motivul pentru care eșecul devine raportat ca 'it la întâmplare mă logat afară şi niciodată ca un incident. Plus o alertă de certificat (rece-Traefik auto-semnat eșec nu a avut nici un detector la toate), un blocat Alertă rollout, și unul pentru un Deplasment așezat sub numărul său dorit de replica replica: 2 ajută doar în timp ce a doua capsulă este de fapt în sus, și unul care nu este arata sanatos. Verificarea TLS este în mod deliberat pe: un certificat această platformă servește în mod greșit trebuie să nu Sondă, din moment ce este exact ceea ce un Traefik rece folosit pentru a face la fiecare org dintr-o dată. monitorizarea/ nu este un obiectiv de interes comun prin convenţie de lungă durată, astfel încât ambele fişiere să poarte propria linie de aplicare.
· KlusterServices
sesiuni coder pe masina dev, enumerate și resuscitabile
este aici, pentru că aparatul este singurul lucru care știe oricare dintre ele. ## The session index Acasă, exact ca terminalele lor sunt orice smux rulează. Fără masă. același motiv: discul știe deja, este ceea ce CLI însuși reia de la, și Memoized on (mtime, size), and it has to be deţine 444 de sesiuni şi 1,1 GB de transcriere, una dintre ele 205 MB. Doar primul și ultimele 128 KB din fiecare fișier sunt citite: directorul de lucru, filiala și deschiderea promptă sunt în primele înregistrări, titlul generat și rezumatul sunt anexat pe măsură ce sesiunea continuă, și mijlocul este apeluri instrument. Măsurat împotriva magazinul real: 2.8s rece, 17ms cald. ## Two things a transcription cannot say O transcriere nu înregistrează faptul că consola a început, așa că a început o sesiune fără ea rândul un membru doar creat este lipsește din lista ei sunt Privind la, care se citește ca butonul nu au făcut nimic. Și o transcriere nu înregistrează în cazul în care acesta este condus de la, astfel încât o coajă care ruleaza una acum poarta id sesiunea ca o optiune utilizator tmux (@kamo codor); pe lângă titlul care deja locuieşte acolo. Aceasta este întreaga regulă duplicat: Reluarea unei sesiuni care este deja atașat oferă o conversație două capete, cu ambele CLI scris la o transcriere și a doua reluare ceea ce primul este în mijlocul. Prin urmare, listarea crește un domeniu, așa că acum cere pentru șapte Separatorul poate fi imprimat şi un membru îl poate tasta. ## The caller still never provides a command Linia de comandă este compusă aici: calea CLI rezolvată în cadrul contului propria casă, sesiunea id bătut sau găsit pe acest disc, și Unu. Înregistrari pregatite acum transporta un fel de... Trimite-la-AI mana-off, care este singurul tip a fost. Dosarul de lucru este singurul lucru pe care o cerere poate influența și este delimitată de realpath la propria casă a contului. Aceasta nu este o limită de privilegiu terminal deja ruleaza ca acel cont CEREREA poate numi mai mic decât setul la care poate ajunge o persoană. O înregistrare care numeşte un de asemenea, contul poate fi răscumpărat numai prin terminalul contului respectiv. 40 de teste noi, 199 în apartament, toate trec. Indicele este verificat în raport cu Real 444-session magazin, mai degrabă decât meciuri singur. Nave de instalare-agent.sh, nu de CI: împingând acest lucru nu trimite nimic la VM.
· KlusterServices
Buget fiecare scurgere, și nu reușesc o implementare care regresează siguranța rollout
PodDiruptionBudgets pentru cele 25 de Desfășurări care rulează acum cu adevărat două replici. minAvailable: 1 lasă un canal să evacueze o capsulă şi să aştepte înlocuirea ei în loc să dea jos un nod întreg. imediat. Kamo namespace a avut ZERO de aceste în timp ce room-service, kamo-univers, ratestack, Longhorn-sistem și Kubevirt toate le-au avut Apar doar Desfăşurari la două replici. Un PDB pe o singură utilizare este mai rău decât niciuna: minAvailable: 1 împotriva reproducerilor: 1 permite zero întreruperi, astfel încât nu protejează Munca, blochează întreţinerea nodului pentru totdeauna. README.md spune așa, și numește deliberat Singletons care nu trebuie să fie listate. script-uri/audit-administrare-siguranţă.sh nu reuşeşte desfăşurarea în cazul în care o desfăşurare în kamo a drift înapoi la o formă care scade cererile în timpul unui rollout: nici o sondă de pregătire, nici un cârlig preStop, nici un minutReadySeconds, Recreați fără un opt-out, sau două replici cu nimic răspândirea-le peste noduri. Acesta auditează mai degrabă CLUSTER decât manifestele. Aceste setări trăiesc în 48 de depozite separate cu 48 de fluxuri de lucru; un paznic per-repo ar prinde o regresie mai devreme, dar este 48 de copii pentru a păstra în etapă și orice repo care nu o adoptă este scutită în tăcere. Fiecare dintre aceste manifestari se termina Aici. Comerțul este că este o alarmă de fum, mai degrabă decât o blocare raportează după fapt, așa că Amprentă întreaga masă și nume ce plutea în derivă mai degrabă decât doar eșec. Exceptările sunt adnotări privind desfășurarea în sine și un motiv este obligatoriu: o scutire fără unul este un eșec. Aceasta este ceea ce permite următoarea persoană spune un singurton deliberat (kamodaemon leagă exclusiv NATS durabile) de ceva nimeni nu a primit rotund la.
· RAGService
Partajați consumatorii indexați și executați două capsule
OARservice leagă două JetStream durabile din propriul său un consumator de împingere durabilă fără nici un grup livra admite exact un abonat. Un al doilea pod ar au fost refuzate [SUB-90012], lăsând indexarea fixată la oricare dintre pod legat în primul rând cu Nimic nu preia controlul atunci când pod este cel care moare. Ambele se alătură acum unui grup de livrare, astfel încât NATS mâinile fiecare eveniment la exact o capsulă. Aceasta este ceea ce indexarea are nevoie de: o dată, nici o dată pe pod, și nu o dată pe pod care sa întâmplat să câștige o cursă. replica 1 - > 2. Acest serviciu nu ruleaza nici o lucrare @Scheduled si nu leaga NATS exclusiv durabil, asa ca a al doilea pod nu copiază nimic este apatrizi cerere de servire, și singurul lucru care ține la O replică a fost că nimic nu a ridicat numărul. La o replica maxSurge ascunde expunerea in timpul unei implementari curate si nicaieri altundeva: un OOM ucide, a Sonda de viata esuata sau o scurgere a nodului a luat capacitatea in intregime pentru 59-79 secunde a Contextul de primăvară ia pentru a porni. Un PodDiruptionBudget din KlusterServices aşteaptă acum o scurgere.
· VectorService
Rulați două capsule
replica 1 - > 2. Acest serviciu nu ruleaza nici o lucrare @Scheduled si nu leaga NATS exclusiv durabil, asa ca a al doilea pod nu copiază nimic este apatrizi cerere de servire, și singurul lucru care ține la O replică a fost că nimic nu a ridicat numărul. La o replica maxSurge ascunde expunerea in timpul unei implementari curate si nicaieri altundeva: un OOM ucide, a Sonda de viata esuata sau o scurgere a nodului a luat capacitatea in intregime pentru 59-79 secunde a Contextul de primăvară ia pentru a porni. Un PodDiruptionBudget din KlusterServices aşteaptă acum o scurgere.
· ConversionService
Rulați două capsule
replica 1 - > 2. Acest serviciu nu ruleaza nici o lucrare @Scheduled si nu leaga NATS exclusiv durabil, asa ca a al doilea pod nu copiază nimic este apatrizi cerere de servire, și singurul lucru care ține la O replică a fost că nimic nu a ridicat numărul. La o replica maxSurge ascunde expunerea in timpul unei implementari curate si nicaieri altundeva: un OOM ucide, a Sonda de viata esuata sau o scurgere a nodului a luat capacitatea in intregime pentru 59-79 secunde a Contextul de primăvară ia pentru a porni. Un PodDiruptionBudget din KlusterServices aşteaptă acum o scurgere.
· APIService
Rulați două capsule
replica 1 - > 2. Acest serviciu nu ruleaza nici o lucrare @Scheduled si nu leaga NATS exclusiv durabil, asa ca a al doilea pod nu copiază nimic este apatrizi cerere de servire, și singurul lucru care ține la O replică a fost că nimic nu a ridicat numărul. La o replica maxSurge ascunde expunerea in timpul unei implementari curate si nicaieri altundeva: un OOM ucide, a Sonda de viata esuata sau o scurgere a nodului a luat capacitatea in intregime pentru 59-79 secunde a Contextul de primăvară ia pentru a porni. Un PodDiruptionBudget din KlusterServices aşteaptă acum o scurgere.
· kamo-internal
Răspundeţi la o sondă de pregătire şi executaţi două capsule
Această desfăşurare nu a avut o sondă de pregătire, aşa că o capsulă a numărat ca Gata în momentul containerul său A început procesul. Cu maxIndisponibil 0 Kubernetes citește că, ca "nou pod este de servire" și se retrage cel vechi Cereri depuse într-un port pe care nu-l asculta nimic, de acolo au venit 502-urile intermitente. /api/health answers only for this pod and deliberatly touchs no backend: a ready probe decide dacă această capsulă părăsește Serviciul, și cablându-l la un suport transformă un blip backend într-o Reporneşte fiecare capsulă de aici. Scurgerea şi perioada de graţie 660 au fost deja aici; sonda lipsă este ceea ce i-a oprit Numărătoare. Kubernetes a fost retragerea pod de scurgere înainte de înlocuire ar putea servi. replica 1 -> 2. Starea server-side trăiește în Redis, nu în pod, și nu există nici o lucrare programată pentru a duplicate, astfel încât o a doua replică nu schimbă nimic, cu excepția faptului că pierderea unui pod încetează să mai fie o pană de curent. La o replica o ucidere OOM, o sondă liveness eșuat sau un canal de scurgere nod luat totul în jos pentru Atâta timp cât este nevoie pentru a boot. topologieSpreadConstraints (adăugat mai devreme, inert până acum) păstrează cele două pe noduri diferite în cazul în care clusterul îl poate gestiona, iar un PodDiruptionBudget din KlusterServices aşteaptă o scurgere.
· kamo-marketing
Se scurge pe SIGTERM, și rula două capsule
Următorul mâner SIGTERM cu un proces de gol.ieșire(143), astfel încât fiecare implementa resetat orice acest pod a avut în zbor Invizibil atunci când cererea a durat milisecunde; nu a fost invizibilă pe un grup care se desfășoară pe fiecare împingere. script-uri / standalone-entertry.cjs împachetează serverul independent: pe SIGTERM se oprește acceptarea, scade inactiv keep-vives la o dată astfel încât o capsulă cu nimic în zbor încă iese în aproximativ o secundă, și permite să ruleze solicită să se termine sub un capac care stă mai jos de terminareGracePeriodSeconds. Portată din kamo-internă, care a rulat-o în producție de luni de zile. Ambalajul pornește server-nonce.js mai degrabă decât server.js, prin NEXT SERVER ENTRY Serverul rămâne exact unde era în lanţ. replica 1 -> 2. Starea server-side trăiește în Redis, nu în pod, și nu există nici o lucrare programată pentru a duplicate, astfel încât o a doua replică nu schimbă nimic, cu excepția faptului că pierderea unui pod încetează să mai fie o pană de curent. La o replica o ucidere OOM, o sondă liveness eșuat sau un canal de scurgere nod luat totul în jos pentru Atâta timp cât este nevoie pentru a boot. topologieSpreadConstraints (adăugat mai devreme, inert până acum) păstrează cele două pe noduri diferite în cazul în care clusterul îl poate gestiona, iar un PodDiruptionBudget din KlusterServices aşteaptă o scurgere.
· kamo-capcha
Rulați două capsule
replica 1 -> 2. Starea server-side trăiește în Redis, nu în pod, și nu există nici o lucrare programată pentru a duplicate, astfel încât o a doua replică nu schimbă nimic, cu excepția faptului că pierderea unui pod încetează să mai fie o pană de curent. La o replica o ucidere OOM, o sondă liveness eșuat sau un canal de scurgere nod luat totul în jos pentru Atâta timp cât este nevoie pentru a boot. topologieSpreadConstraints (adăugat mai devreme, inert până acum) păstrează cele două pe noduri diferite în cazul în care clusterul îl poate gestiona, iar un PodDiruptionBudget din KlusterServices aşteaptă o scurgere.
· kamo-register
Se scurge pe SIGTERM, răspunde la o sondă de pregătire, și rulați două capsule
Următorul mâner SIGTERM cu un proces de gol.ieșire(143), astfel încât fiecare implementa resetat orice acest pod a avut în zbor Invizibil atunci când cererea a durat milisecunde; nu a fost invizibilă pe un grup care se desfășoară pe fiecare împingere. script-uri / standalone-entertry.cjs împachetează serverul independent: pe SIGTERM se oprește acceptarea, scade inactiv keep-vives la o dată astfel încât o capsulă cu nimic în zbor încă iese în aproximativ o secundă, și permite să ruleze solicită să se termine sub un capac care stă mai jos de terminareGracePeriodSeconds. Portată din kamo-internă, care a rulat-o în producție de luni de zile. Această desfăşurare nu a avut o sondă de pregătire, aşa că o capsulă a numărat ca Gata în momentul containerul său A început procesul. Cu maxIndisponibil 0 Kubernetes citește că, ca "nou pod este de servire" și se retrage cel vechi Cereri depuse într-un port pe care nu-l asculta nimic, de acolo au venit 502-urile intermitente. /api/health answers only for this pod and deliberatly touchs no backend: a ready probe decide dacă această capsulă părăsește Serviciul, și cablându-l la un suport transformă un blip backend într-o Reporneşte fiecare capsulă de aici. Fişierul DockerHANITYCHECK a indicat /api/health deoarece a fost scris; traseul nu există, astfel încât acest cec a eşuat atâta timp cât a fost acolo. replica 1 -> 2. Starea server-side trăiește în Redis, nu în pod, și nu există nici o lucrare programată pentru a duplicate, astfel încât o a doua replică nu schimbă nimic, cu excepția faptului că pierderea unui pod încetează să mai fie o pană de curent. La o replica o ucidere OOM, o sondă liveness eșuat sau un canal de scurgere nod luat totul în jos pentru Atâta timp cât este nevoie pentru a boot. topologieSpreadConstraints (adăugat mai devreme, inert până acum) păstrează cele două pe noduri diferite în cazul în care clusterul îl poate gestiona, iar un PodDiruptionBudget din KlusterServices aşteaptă o scurgere.
· kamo-login
Se scurge pe SIGTERM, răspunde la o sondă de pregătire, și rulați două capsule
Următorul mâner SIGTERM cu un proces de gol.ieșire(143), astfel încât fiecare implementa resetat orice acest pod a avut în zbor Invizibil atunci când cererea a durat milisecunde; nu a fost invizibilă pe un grup care se desfășoară pe fiecare împingere. script-uri / standalone-entertry.cjs împachetează serverul independent: pe SIGTERM se oprește acceptarea, scade inactiv keep-vives la o dată astfel încât o capsulă cu nimic în zbor încă iese în aproximativ o secundă, și permite să ruleze solicită să se termine sub un capac care stă mai jos de terminareGracePeriodSeconds. Portată din kamo-internă, care a rulat-o în producție de luni de zile. Această desfăşurare nu a avut o sondă de pregătire, aşa că o capsulă a numărat ca Gata în momentul containerul său A început procesul. Cu maxIndisponibil 0 Kubernetes citește că, ca "nou pod este de servire" și se retrage cel vechi Cereri depuse într-un port pe care nu-l asculta nimic, de acolo au venit 502-urile intermitente. /api/health answers only for this pod and deliberatly touchs no backend: a ready probe decide dacă această capsulă părăsește Serviciul, și cablându-l la un suport transformă un blip backend într-o Reporneşte fiecare capsulă de aici. replica 1 -> 2. Starea server-side trăiește în Redis, nu în pod, și nu există nici o lucrare programată pentru a duplicate, astfel încât o a doua replică nu schimbă nimic, cu excepția faptului că pierderea unui pod încetează să mai fie o pană de curent. La o replica o ucidere OOM, o sondă liveness eșuat sau un canal de scurgere nod luat totul în jos pentru Atâta timp cât este nevoie pentru a boot. topologieSpreadConstraints (adăugat mai devreme, inert până acum) păstrează cele două pe noduri diferite în cazul în care clusterul îl poate gestiona, iar un PodDiruptionBudget din KlusterServices aşteaptă o scurgere.
· kamo-nowww
Se scurge pe SIGTERM, și răspunde la o sondă de pregătire
Următorul mâner SIGTERM cu un proces de gol.ieșire(143), astfel încât fiecare implementa resetat orice acest pod a avut în zbor Invizibil atunci când cererea a durat milisecunde; nu a fost invizibilă pe un grup care se desfășoară pe fiecare împingere. script-uri / standalone-entertry.cjs împachetează serverul independent: pe SIGTERM se oprește acceptarea, scade inactiv keep-vives la o dată astfel încât o capsulă cu nimic în zbor încă iese în aproximativ o secundă, și permite să ruleze solicită să se termine sub un capac care stă mai jos de terminareGracePeriodSeconds. Portată din kamo-internă, care a rulat-o în producție de luni de zile. Această desfăşurare nu a avut o sondă de pregătire, aşa că o capsulă a numărat ca Gata în momentul containerul său A început procesul. Cu maxIndisponibil 0 Kubernetes citește că, ca "nou pod este de servire" și se retrage cel vechi Cereri depuse într-un port pe care nu-l asculta nimic, de acolo au venit 502-urile intermitente. /api/health answers only for this pod and deliberatly touchs no backend: a ready probe decide dacă această capsulă părăsește Serviciul, și cablându-l la un suport transformă un blip backend într-o Reporneşte fiecare capsulă de aici. Deja la două replici; aceasta este ceea ce face a doua acoperă de fapt prima în timpul unei Rularea mai degrabă decât ambele fiind înlocuite orb.
· kamo-shared-library
O blocare distribuită, astfel încât o verificare programată poate supraviețui unei a doua replica
Nu există nici un ShedLock oriunde pe această platformă și nu a existat niciodată. Toate 97 @ Scheduled matura pe 18 servicii sunt corecte doar pentru că fiecare serviciu rulează exact o capsulă. Scalați oricare dintre le și matura rulează de două ori o căpușă Anunțuri, două trece pe o perioadă de facturare. Implementarea unei singure replici nu a fost doar ne-scalat; numărul replica a fost rulment de sarcină, și codul spune acest lucru în trei locuri deja. Are un cadavru exact pe o capsulă. Un apel explicit, nu o annotare. @SingletonTask linga @Scheduled este designul evident și este o capcană aici: ar fi aplicată de un proxy AOP, și un proxy este ocolit în întregime atunci când o metodă este atins din interiorul aceeași fasole prins de cinci ori diferite. O matura care si-a pierdut lacatul si a fugit pe fiecare pod este ca Același eșec, și nimic nu l-ar dezvălui până când un client a numărat e-mailurile lor. Luarea de blocare de numindu-l nu poate fi ocolit de modul în care metoda a fost atins, și greps. Redis, nu baza de date. O masă de blocare are nevoie de DDL, și DDL pe acest grup face fiecare serviciu răspunde 500 "Nepotrivirea versiunii de schemă" pentru ~15 minute în timp ce conexiunile Hikari îmbătrânesc. Plata Până la instalarea utilajelor care împiedică întreruperile platformei este un comerț prost atunci când Redis este deja desfășurat. Eşecuri închise. În cazul în care Redis este imposibil de atins sarcina este omisă, nu a alerga: pentru munca care trimite e-mail sau Mută banii, rulează de două ori este mai rău decât să nu ruleze, și următorul tic retries. Lansarea este o compara-și-șterge script-ul astfel încât un organism care a depășit leasingul nu poate șterge un alt pod are din moment ce luat, și depășirea este logat la ERROR pentru că nimic altceva nu ar dezvălui. În cazul în care un serviciu nu are nici o Redis, retragerea ruleaza corpul întotdeauna Prin urmare, adoptarea acestui lucru nu schimbă niciodată comportamentul pe cont propriu și avertizează la pornire. Gardianul manifest în CI refuză replicile > 1 pentru un serviciu în această stare. 7 teste; apartament complet 2710 verde.
· kamo-register
Vopsea fiecare logo de pe site-ul de înscriere în culoarea orgii
Orgul alege "Site-ul de înregistrare Logo Overlay" în kamo-internă lângă pagina de start și suprapuse ecran de încărcare. Un set pentru întregul site: fiecare pas al fluxului de înregistrare, organizația și ecranele echipei, ecranul complet și ecranul închis. Deci TemaLogo nu ia nici o recuzită pentru ea și citește culoarea în sine, modul în care aceasta deja citeşte identitatea logo-ului companiei-vs-software-ului - config.json is Singura sursă a acestui site de branding org, și o recuzită ar fi una mai mult lucru pe care fiecare site de apel trebuie să-și amintească. Culoarea sosește deja rezolvată: kamo-internă stochează un TOKEN astfel încât org că rebrands menține o suprapunere de potrivire, și serviciul de securitate rezolvă e împotriva paletei acelei orgi când scrie config. Json. Nimic aici. are nevoie de vocabularul de 13 token. O valoare CSS nu poate parse este scăzut mai degrabă decât a trecut prin - un invalid fundal-color vopsea mascat cutie transparentă, care citește ca un logo care a dispărut. O mască, nu un filtru: un filtru poate împinge doar culorile existente ale logo-ului în jurul și nu pot atinge o țintă arbitrară. Rămâi montate şi Pur şi simplu invizibilă, aşa că îşi ţine cutia exactă pentru ca masca să o umple, Lăsând layout fiecare apelantului neschimbat. Coloana proprie și propria cheie config.json, separate de sign-in site-ul: cele două sunt marcate independent, și o org care etichete albe una nu trebuie să fie albă pe cealaltă.
· kamo-login
Vopsea fiecare logo de pe site-ul de sign-in în culoarea orgii
Orgul alege "Login Site Logo Overlay" în kamo-internă lângă casă Pagini și suprapuse ecran de încărcare. Un cadru pentru întregul site: formular sign-in, picker spațiu de lucru, recuperare parolă și resetare, și ecranul semn-out sunt același logo pe același fundal, și nimeni nu vrea pentru a seta umbra de cinci ori. Deci TemaLogo nu ia nici o recuzită pentru ea și citește culoarea în sine, modul în care aceasta deja citeşte identitatea logo-ului companiei-vs-software-ului - config.json is Singura sursă a acestui site de branding org, și o recuzită ar fi una mai mult lucru pe care fiecare site de apel trebuie să-și amintească. Culoarea sosește deja rezolvată: kamo-internă stochează un TOKEN astfel încât org că rebrands menține o suprapunere de potrivire, și serviciul de securitate rezolvă e împotriva paletei acelei orgi când scrie config. Json. Nimic aici. are nevoie de vocabularul de 13 token. O valoare CSS nu poate parse este scăzut mai degrabă decât a trecut prin - un invalid fundal-color vopsea mascat cutie transparentă, care citește ca un logo care a dispărut. O mască, nu un filtru: un filtru poate împinge doar culorile existente ale logo-ului în jurul și nu pot atinge o țintă arbitrară. Rămâi montate şi Doar invizibil, astfel încât încă se trage pe Load (singura sursă de naturalSize) și pe Error (care merge pe logo-ul complet de rezervă) și așa Încă mai are cutia exactă pe care să o umple masca. Fiecare apelantului aspect este neschimbat. Micul semn animat din pastila "Putere de KamoCRM" este propria org logo-ul de asemenea, astfel încât urmează setarea - doar redă prin următoarea / imagine pentru că sarcina de pilula dornic, astfel încât să poarte masca în sine mai degrabă decât devenind un ThemeLogo. Altfel ar fi fost singurul logo de pe site-ul a ratat setarea.
· kamo-internal
Alegeți o suprapunere logo pentru site-urile de sign-in și de sign-up
O a patra și a cincea suprafață lângă pagina principală, ecran de încărcare și KamoMeets suprapuse, împărtășind vocabularul lor de treisprezece token și viața lor specimene. Ambele sunt întotdeauna oferite, spre deosebire de kamoMeets unul: fiecare org are un semn-in și un site de înscriere indiferent de aplicații pe care le rulează, și un org fără domeniu al acestuia proprii încă le ajunge pe login./register.kamotrm.com pictat în sine Marcare. Ele sunt, de asemenea, singurele două suprapuse aici care nu se aplică în momentul în care acestea sunt salvate. kamo-login și kamo-register nu au nici OrgContext pentru a citi, așa Serviciul de securitate rezolvă jetonul și coace culoarea în temă config.json aceste site-uri aduc; schimbarea terenurilor atunci când furnizarea rescrie acel fişier, pe care acelaşi salvator îl declanşează deja. Cinci picături aproape-identice ar fi fost cinci locuri pentru a pluti, astfel încât bloc editor este acum un rendeOverlaySelect () suprafața de partajare - a șasea Suprafaţa este un apel mai degrabă decât un alt douăzeci de linii. Rândurile numai pentru citire Deja le-am împărţit pe ale lor. Cele două terenuri plimbare Organizație. de la JSON ca fraţii lor, cu o test de călătorie rotundă: mapper rulează de două ori pe drum spre browser, și a Field nu poate citi înapoi de la propria ieșire este un cadru care în tăcere Returnează la următoarea încărcătură.
· MediaService
Asigurați Mediaservice corecta pe mai mult de o capsulă, și rula două
MediaService a rulat doar o singură replică, aşa că o desfăşurare a acesteia a fost o pană totală de chat şi un OM a fost la fel. Nu a putut rula două, din motive care au fost toate în cod: 1. BROCKER furtun este în-imagine. permiteSimpluBroker înseamnă convertiAndTrimite ajunge doar WebSockets conectat la naveta care face apelul. 13 controlori de releu deja manipulat acest drept prin abonarea la NAT cu un dispecer simplu (nici un grup coada, astfel încât fiecare pod primește fiecare mesaj) și re-publicare la nivel local. Treisprezece alte site-uri de apel nu au tastat indicatori, prezenţă, insigne necitite, raspunsuri, oferta WebRTC/raspuns/ICE, notificări de intrare-chat şi fiecare ar fi livrat aproape jumătate din audienţă. StompFanout generalizează modelul pe care aceste relee îl folosesc deja: publică {destinaţia, sarcina utilă} on un subiect NATS de bază, fiecare pod îl releu în propriul broker. Central NATS, nu JetStream, deoarece acestea sunt evenimente live-moment cu nici o valoare redată și nici un flux care acoperă subiectul. Deoarece nucleul NATS oferă, de asemenea, editorului, trimite () NU scrie la nivel local, precum și că ar livra de două ori aici. Cu NATS jos se încadrează înapoi la o trimitere locală, care este ceea ce platforma a făcut înainte. 2. Consumatorul per-conversatie a fost un DURABIL EXCLUSIV. ChatSessionAbonamentManager numit JetStream durabil după sesiunea de chat GUID, și un consum de împingere durabil admite exact unul abonat. A doua legătură a podului a fost refuzată [SUB-90012] și fiecare membru a cărui priză a aterizat nu s-a primit nici un mesaj, nici chitanţe citite, nici evenimente adăugate membrilor, nici o eroare. Acum un consumator efemer: nici un nume pentru a ciocni peste, unul pe pod, cules de server. Rejudecarea asta a inventat un nou nume durabil în caz de coliziune. A funcţionat şi s-a scurs. Consumator permanent de servere per coliziune care nu a şters nimic. 3. CINCI MAI MULTE DURABLE DE NAME, care necesită tratament opus în funcție de locul de muncă. Conversia şi releele VOIP STOMP au trecut prin JetStream mai degrabă decât un dispecer de bază, aşa că au avut acelaşi Insecta exclusivitatii este acum efemera, deoarece fiecare pod trebuie sa primeasca. Notificatorul de chat-email, social consumatorii de intrare și de conversie de marketing trebuie să ruleze exact o dată, astfel încât acestea să păstreze lor durabil și să se alăture unui grup livra, ceea ce înseamnă, de asemenea, un supraviețuitor preia lucrarea atunci când o capsulă moare. Sunt necesare sesiuni lipicioase pe ruta media, nu opţionale: aceşti clienţi STOMP folosesc SockJS, al cărui flux xhr/xhr-polling Fallback este câteva cereri HTTP în picioare pentru o conexiune împotriva stării sesiunii care trăieşte în memoria unei capsule. Robina rotundă o sparge. A se vedea Ingressroute.yaml. replica 1 -> 2. 367 teste trec.
· SecurityService
Se coace logo-ul de sign-in și înscrie-up suprapuse în config.json
kamo-login și kamo-register rulează pe propriile lor gazde fără OrgContext, așa acest fișier este singura cale de branding lor ia - același KamoMeet Deja se foloseşte. Jetonul este rezolvat împotriva paletei de org AICI și hex terminat este scris, care ține vocabularul de treisprezece token din Încă două baze de cod care ar avea nevoie fiecare de propria lor copie. Coacerea unei culori rezolvate nu este greşeala pe care jetonul stocat o evită: aceasta fișier este regenerat pe fiecare branding salva, modificări de culoare incluse, așa valoarea nu poate rămâne în urmă. Absent inseamna fara acoperire, deci un org care nu a ales nici o castiga nici o cheie si Logourile sale sunt neatinse, adică fiecare org până la alegerea unuia. Testele acoperă cele trei suprafeţe situate în afară: ele împart un constructor şi o org suprapus site-ul său de sign-in nu trebuie să aibă logo-ul KamoMeet pictat ca efect secundar.
· kamo-shared-library
O suprapunere logo pentru site-urile de sign-in și de sign-up
Încă două coloane din vocabularul home logo overlay deja stabilite: un simbol, nu un hex, astfel încât o org care rebrands păstrează un supraplay potrivire sale culori noi. Ambele nulable cu nici un backfill - nul este NONE, care este ceea ce Fiecare orga arata ca astazi. Coloane separate mai degrabă decât un cadru comun, din același motiv pagina de start și ecranul de încărcare au propria lor: cele două site-uri sunt marcate în mod independent și o org care nu trebuie să fie albă Celălalt. PatchOrganization este un ALLOW-LIST, astfel încât numele merg acolo. O coloană acest serviciu se deserializeaza fericit dar aceasta metoda nu se numeste acceptate cu un 200 și niciodată stocate în tăcere - nici o eroare, nici o linie de jurnal, şi singurul simptom este o setare care revine la următoarea sarcină.
· kamo-login
Răspundeți la link într-o scrisoare de modificare a adresei
/Verify-email-schimbare cheltuie semnul într-o scrisoare de schimbare-adresă și spune ce s-a întâmplat. Locuieşte aici mai degrabă decât în spaţiul de lucru pentru că scrisoarea merge la o căsuţă poştală PERSONALă citită de obicei pe un telefon care nu a semnat niciodată în spațiul de lucru, și de multe ori nu semnat în nimic. login. gazda care întotdeauna rezolvă pentru o organizație și nu are nevoie de o sesiune, astfel încât este singurul loc unde legătura este sigură să lucreze pentru persoana care are cea mai mare nevoie de ea. Nimic de pe pagină nu cere nimic. Soseşte, cheltuieşte jetonul şi rapoarte Cutia poştală au deschis-o. Cheltuielile sunt păzite împotriva dezvoltării Reactului dublu-invoce, deoarece jetonul este de unică folosință și un al doilea apel ar cheltui și apoi să raporteze succesul ca "această legătură nu mai este valabilă." Releul de lângă /api/recoperă și modelează în același mod: SecurityService is Numai ClusterIP, așa că trece peste cluster DNS, care face Gazduieste numele serviciului și pune numele gazda browser-ului în X-Forwarded-Host backend-ul îl citeşte.
· kamo-internal
Schimbați propria adresă și o filă de platformă pentru fiecare cont
Două suprafeţe, unite printr-o singură distincţie, platforma nu a explicat niciodată oricine: un cont de utilizator este persoana, și un membru este o organizație relaţia cu acea persoană. Profilul meu câștigă un card de e-mail personal, în camera de acces lângă parolă pentru că este ceea ce este acceptă, și adresa o resetare parola este trimis la. Întocmirea ei sub contact Detaliile ar fi depunerea unei chei sub papetărie. Se angajează mai degrabă decât trece prin bara de salvare a camerei: o schimbare este o cerere, o scrisoare și un confirmare, și un buton Salvare care a scris un identificator de sign-in fără dovadă Cutia poștală există ar fi întreaga problemă într-un singur control. Contul apartine organizatiei numite de security provider id, si numai care o poate schimba. Prin urmare, cardul este vizibil peste tot și editabil în un loc ascuns în celelalte ar lăsa un membru de vânătoare pentru un cadru care pare să fi fost șterse. Unde nu poate fi editat scrie al cui este este, și oferă ambele căi de întoarcere: trecerea din meniul din stânga sus fără a părăsi aplicația, și semnarea în direct la propria adresă de sign-in organizației (sau, atunci când nu are încă, la cel comun, numit ca atare). Ambele provin de la OrgEntryDomainRezolver, deci gazda nu este niciodată una care nu reușește să rezolve. Un explicator stă sub carte pe fiecare organizație, nu numai în interiorul Refuz. "Contul de utilizare" versus "contul de membru" este o distincție nimeni în afara platforma are orice motiv să știe, și devine portantă la exact moment cineva este refuzat ceva care este cel mai rău timp posibil pentru a satisface o idee pentru prima dată. /cont câștigă o a patra filă, Utilizatori, poartă pe noua platformă MANAGE USERS Corect. Dreptul poartă deja "organizaţia curentă trebuie să fie platforma organization" previous sesiunea în interiorul unui chiriaș, astfel încât fila nu verifică organizația separat; o a doua autoritate acolo ar putea fi de acord doar vreodată cu serverului. Căutare și paging sunt prea serverului: propriul filtru de rețea caută rândurile se întâmplă să dețină, care pe un tabel la nivelul platformei înseamnă raportarea "niciun rezultat" pentru o cont care există. Sertarul din spatele unui rând este legat de consecinţe mai degrabă decât de subiect. Identitate editează într-un proiect și salvează o dată; starea contului se angajează per comutator și rapoarte câte şedinţe au mers cu ea; recuperarea este separată şi confirmată. Doamne. conturi și utilizatorul de sistem redă numai citire, deoarece serverul le refuză şi compunerea unei editări care nu va ateriza niciodată e mai rău decât să ţi se spună. Mai întâi. De asemenea, fixează banner-ul de securitate care a fost pe această pagină tot timpul. Acesta a construit link-ul său de la nume gazdă care nu este gazda spaţiului de lucru, la un membru id care nu există peste Acolo. Acum indică ecranul de sign-in pe care serverul îl rezolvă.
· kamo-shared-library
Adăugați un subscriptor grup coadă, astfel încât munca este împărtășită mai degrabă decât fixat la un pod
Însoţitor de a abona efemerul. Două locuri de muncă diferite, doi consumatori diferiți: abonațiEfemeral fiecare pod trebuie să vadă fiecare mesaj (orice se termină într-o emisiune STOMP) abonaţiQueue lucrarea trebuie să aibă loc exact ODATĂ (trimiţând un e-mail, chemând un furnizor) Nici ceea ce face o simplă replică nu este durabil. Cu nici un grup livra un impuls durabil consumatorul admite un singur abonat, astfel încât al doilea pod este refuzat [SUB-90012] și munca este fixat la care pod legat primul Adăugarea unui grup de livrare transformă același lucru durabil într-o coadă cota păstăi, menținând relivrarea și MaxDeliver exact cum erau. ConciliationDeliverGroup oglindește compilația existentăDurableFilter și nu este opțională: fiecare consumator această schimbare convertește deja există pe fluxul în forma veche, și jnats nu va să se alăture unui simplu durabil ca coadă. Fără reconciliere, prima desfăşurare după aceasta ar pleca. aceste abonamente permanent mort în loc de împărtășit S-a scris despre reconcilierea filtrului. Bucla de livrare este acum o metodă în spatele tuturor celor trei puncte de intrare.
· kamo-shared-library
Adăugați un subscripție efemeră, astfel încât mai multe pod poate servi o conversație
Un consumator de împingere durabilă admite exact un abonat. A doua capsulă care leagă acelaşi nume este refuzat cu [SUB-90012] Consumatorul este deja obligat la un abonament, și nimic nu retries. ChatSessionAbonamentManager nume sale durabile după sesiunea de chat GUID, astfel încât cu două Capsulele MediaService oricare ar fi legat o conversație mai întâi ar deține în tăcere și fiecare membru Al cui WebSocket a aterizat pe cealaltă capsulă nu va primi nimic. Fără mesaje, fără chitanţe citite. evenimente cu membri adăugați, nici o eroare. Această proprietate unică este motivul pentru care MediaService a fost fixat la unul Replica, și de ce o implementare a acesteia este o pană totală de chat mai degrabă decât o răsturnare. subscrieEfemeral() creează în schimb un consumator anonim. Nu există nici o identitate inter-pod pentru a ciocni peste, astfel încât fiecare pod primește fiecare mesaj și fani propriilor clienți STOMP În-Hap broker simplu necesită. Păstrează filtrulSubiect calea durabilă învățată pentru a seta (un filtru gol consumă întregul flux), și adaugă un inactivThreshold astfel încât un pod care este SIGKILLed fără a se dezabona nu lasă consumatorul său pe râu pentru totdeauna. Nici o schimbare de comportament pentru apelanții existenți: subscrie () este neatins, și bucla de livrare ambele utilizarea căi este acum o metodă astfel încât acestea nu pot deriva. Consumatorii efemeri nu redau mesajele publicate în timp ce nimic nu a fost subscris. Nici pe cele durabile, ambele folosesc DeliverPolicy. Nou .
· kamo-internal
"Ce s-a întâmplat?" umple fereastra, deci maximizarea o măreşte
Fiecare alt câmp al formularului de raport este dimensionat de conținutul acestuia. ă Descrierea este cea cineva este de fapt scris în, așa că acum este nevoie orice inaltime este lasat Merită făcut. Trei părţi, şi toate trei sunt necesare: Zona de defilare câștigă o coloană flex interior la minHeight: 100% Înălţimea: 100%. La o înălţime fixă, o formă mai înaltă decât fereastra. ar comprima propriile câmpuri, deoarece copiii flexibili se micșorează în mod implicit. Ca un minim este exact înălţimea completă atunci când există loc pentru a rezerva care este camera pe care descrierea o ia şi creşte dincolo de ea atunci când există nu, moment în care pergamentul cutiei exterioare și nimic nu este zdrobit. Câmpul devine un element flex cu un etaj de minHeight. S-a dus. cu ea: care conduce TextareaAutolize, care măsoară conținutul și scrie o inaltime de linie, si cu să nu fie nimic care să oprească căderea câmpului la zero. Textarea este lansata cu inaltime: automat!important, NU Înălţimea: 100%. Învelişul conturat îşi întinde copiii, aşa că textarea ar umple-o fără ajutor cross-size nu se întinde, și autosize-l oferă o linie. Setare Înălţimea înapoi la auto restaurează întinderea în loc să se lupte cu ea. 100% a fost încercat în primul rând și în tăcere nu face nimic la toate: procentul nu are conţinând bloc pentru a rezolva împotriva, astfel încât acesta cade înapoi la auto Câmpul face două rânduri intrinsece, fără nici o eroare. pentru a observa. Măsurat, nu presupus. Un ham păpuşar construit din această componentă propria ieșire emoție oferă 240px de textarea într-o fereastră 720px, 444px la 940px, și un câmp cu podea cu forma defilare la 430px Câmpul de titlu încă 40px în toate trei. Gardianul angajat afirmă regula mai degrabă decât înălțimea, deoarece jsdom nu aspect, și a fost confirmat de a eșua atunci când regula este revenit la 100%.

Universul tău te așteaptă.
Începeți gratuit în câteva minute.Adăugați produse oricând sunteți pregătit — fără migrare, fără autentificări noi.
Gratuit pentru totdeauna — nu este necesar un card de credit