socialgekon.com
  • Tärkein
  • Suunnitteluprosessi
  • Muokkaus
  • Prosessi Ja Työkalut
  • Sijoittajat Ja Rahoitus
Taustaa

Aikaleiman katkaisu: Ruby on Rails ActiveRecord Tale

Testien on tarkoitus auttaa pitämään sovellukset hilseilemästä. Mutta joskus testeistä voi tulla hiutaleita - jopa suorimmatkin. Näin pääset ongelmalliseen testiin a Ruby on Rails PostgreSQL: n tukema sovellus ja mitä paljastimme.

Halusimme tarkistaa, että tietty liiketoimintalogiikka (jota kutsutaan menetelmällä perform) ei muuta calendar malli (esimerkki Calendar, Ruby on Rails ActiveRecord -malliluokasta), joten kirjoitimme:

let(:calendar) { create(:calendar) } specify do expect do perform # call the business action calendar.reload end .not_to change(calendar, :attributes) end

Tämä oli ohi yhdessä kehitysympäristössä (MacOS), mutta se lähes aina epäonnistui CI: ssä (Linux).



Onneksi onnistuimme toistamaan sen toisessa kehitysympäristössä (Linux), jossa se epäonnistui viestillä:

expected `Calendar#attributes` not to have changed, but did change from {'calendar_auth_id'=>8, 'created_at'=>2020-01-02 13:36:22.459149334 +0000, 'enabled'=>false, 'events_...t_sync_token'=>nil, 'title'=>nil, 'updated_at'=>2020-01-02 13:36:22.459149334 +0000, 'user_id'=>100} to { 'calendar_auth_id'=>8, 'created_at'=>2020-01-02 13:36:22.459149000 +0000, 'enabled'=>false, 'events_...t_sync_token'=>nil, 'title'=>nil, 'updated_at'=>2020-01-02 13:36:22.459149000 +0000, 'user_id'=>100}

Näetkö jotain hämärää?

Tutkimus

Tarkemmin tarkastellessamme huomasimme, että created_at ja updated_at aikaleimoja muutettiin hieman expect: n sisällä lohko:

{'created_at'=>2020-01-02 13:36:22.459149334 +0000, 'updated_at'=>2020-01-02 13:36:22.459149334 +0000} {'created_at'=>2020-01-02 13:36:22.459149000 +0000, 'updated_at'=>2020-01-02 13:36:22.459149000 +0000}

Sekuntien murto-osa katkaistiin siten, että 13:36:22.459149334 tuli 13:36:22.459149000.

Olimme varmoja, että perform ei päivittänyt calendar objektin, joten muodostimme hypoteesin, jonka mukaan tietokanta katkaisi aikaleimat. Tämän testaamiseksi käytimme edistyneintä tunnettua virheenkorjaustekniikkaa, ts. laittaa virheenkorjauksen :

let(:calendar) { create(:calendar) } specify do expect do puts 'before perform: #{calendar.created_at.to_f}' perform puts 'after perform: #{calendar.created_at.to_f}' calendar.reload puts 'after reload: #{calendar.created_at.to_f}' end .not_to change(calendar, :attributes) end

Mutta katkaisu ei ollut näkyvissä tulosteessa:

before perform: 1577983568.550754 after perform: 1577983568.550754 after reload: 1577983568.550754

Tämä oli varsin yllättävää - lisävaruste #created_at olisi pitänyt olla sama arvo kuin attribuutin hash-arvo attributes['created_at'] Varmistaaksemme, että tulostimme saman arvon kuin väitteessä, muutimme tapaa, jolla pääsimme created_at

Sen sijaan, että käyttäisimme lisävarustetta calendar.created_at.to_f, vaihdoimme sen hakemiseen suoraan hash-määritteistä: calendar.attributes['created_at'].to_f. Epäilyksemme calendar.reload vahvistettiin!

before perform: 1577985089.0547702 after perform: 1577985089.0547702 after reload: 1577985089.05477

Kuten näette, soittaminen perform ei muuttunut created_at, mutta reload teki.

Varmista, että muutosta ei tapahtunut toisessa calendar -tapauksessa ja sitten pelastettuna teimme vielä yhden kokeen. Ladasimme uudelleen calendar ennen testiä:

let(:calendar) { create(:calendar).reload } specify do expect do perform calendar.reload end .not_to change(calendar, :attributes) end

Se teki testistä vihreän.

Korjaus

Koska tiedämme, että tietokanta katkaisee aikaleimamme ja epäonnistuu testeissämme, päätimme estää katkaisun. Luomme DateTime pyöristää sen kokonaisiin sekunteihin. Sitten käytimme tätä objektia asettamaan Railsin aktiivisen tietueen aikaleimat nimenomaisesti. Tämä muutos vahvisti ja vakautti testit:

let(:time) { 1.day.ago.round } let(:calendar) { create(:calendar, created_at: time, updated_at: time) } specify do expect do perform calendar.reload end .not_to change(calendar, :attributes) end

Syy

Miksi näin tapahtui? Aktiivisen tietueen aikaleimat ovat kiskojen asettamat ’ActiveRecord::Timestamp moduuli käyttämällä Time.now . Time tarkkuus on Käyttöjärjestelmästä riippuvainen ja kuten asiakirjoissa todetaan, voi sisältää murto-sekunteja .

Testasimme Time.now päätöslauselma MacOS: ssa ja Linuxissa komentosarjalla, joka laskee murto-osan pituuksien taajuudet:

pry> 10000.times.map { Time.now.to_f.to_s.match(/.(d+)/)[1].size }.group_bya.map [k, v.count].to_h # MacOS => {6=>6581, 7=>2682, 5=>662, 4=>67, 3=>7, 2=>1} # Linux => {6=>2399, 7=>7300, 5=>266, 4=>32, 3=>3}

Kuten näette, noin 70%: lla Linuxin aikaleimoista oli tarkkuus seitsemän numeroa desimaalin jälkeen, kun taas MacOS: ssa vain 25%. Tästä syystä testit läpäisivät suurimman osan ajasta MacOS: ssä ja epäonnistuivat suurimman osan ajasta Linuxissa. Olet ehkä huomannut, että testilähdöllä oli yhdeksän numeron tarkkuus - se johtuu RSpecista käyttää Time#nsec muotoilla aikalähtö.

Kun Rails-mallit tallennetaan tietokantaan, kaikki niiden aikaleimat tallennetaan PostgreSQL: n tyypillä nimeltä timestamp without time zone, joka on mikrosekunnin tarkkuus - eli kuusi numeroa desimaalin jälkeen. Joten kun 1577987974.6472975 lähetetään PostgreSQL: ään, se katkaisee murto-osan viimeisen numeron ja tallentaa sen sijaan 1577987974.647297

Kysymykset

On edelleen kysymys miksi calendar.created_at ei ladattu uudelleen, kun soitimme calendar.reload, vaikka calendar.attributes['created_at'] ladattiin uudelleen.

Myös tulokset Time tarkkuustesti ovat hieman yllättäviä. Odotimme, että MacOS: lla suurin tarkkuus on kuusi. Emme tiedä miksi joskus siinä on seitsemän numeroa. Yllätti meidät enemmän viimeisten numeroiden arvon jakautuminen:

pry> 10000.times.map { Time.now}.map t.to_f.to_s.match(/.(d+)/)[1] .select.group_bye.map [k, v.size].to_h # MacOS => {'9'=>536, '1'=>555, '2'=>778, '8'=>807} # Linux => {'5'=>981, '1'=>311, '3'=>1039, '9'=>309, '8'=>989, '6'=>1031, '2'=>979, '7'=>966, '4'=>978}

Kuten näette, MacOS: n seitsemäs numero on aina 1, 2, 8 tai 9.

Jos tiedät vastauksen jompaankumpaan näistä kysymyksistä, jaa selitys meille.

Tulevaisuus

Se, että Ruby on Rails ” Aktiivinen ennätys Aikaleimat luodaan sovelluspuolelle voivat myös vahingoittaa, kun näitä aikaleimoja käytetään tietokantaan tallennettujen tapahtumien luotettavaan ja tarkkaan järjestämiseen. Koska sovelluspalvelimen kellot voidaan synkronoida, tapahtumat järjestetään created_at voivat näkyä eri järjestyksessä kuin ne todellisuudessa tapahtuivat. Saada luotettavampi käyttäytyminen , olisi parempi antaa tietokantapalvelimen hoitaa aikaleimat (esim. PostgreSQL: t now() ).

Se on kuitenkin toisen artikkelin arvoinen tarina.


Erityiskiitokset Gabriele Renzi tämän artikkelin luomisessa.

Perustietojen ymmärtäminen

Mikä on ActiveRecord Ruby-palvelussa?

ActiveRecord on objektisuhdekartoituskirjasto, jonka tarjoaa Ruby on Rails. Sen avulla voit säilyttää objekteja tietokannassa, hakea sitten tallennettuja tietoja ja välittää objekteja.

Mikä on Active Record -toteutus?

Active Record on yritysarkkitehtuurimalli, jota käytetään muistin sisäisten objektien säilyttämiseen relaatiotietokannoissa. Ruby on Rails -toteutuksessa ActiveRecord seuraa muutoksia Rails-mallin määritteisiin. Kun malli on tallennettu, ActiveRecord lähettää muutokset ja tarvittavat aikaleimat tietokantaan.

Miksi aikaleima on tärkeä?

ActiveRecord tallentaa oletusarvoisesti kaksi aikaleimaa: created_at (aika, jolloin malli tallennettiin ensimmäisen kerran) ja updated_at (aika, jolloin malli viimeksi tallennettiin). Ne tarjoavat perustarkastusominaisuudet ja antavat sovellusten lajitella malleja uusimmista tai viimeisimmistä päivitetyistä.

Mikä on aikaleima PostgreSQL: ssä?

Aikaleima on PostgreSQL-tietotyyppi, joka tallentaa sekä päivämäärän että kellonajan. Sillä on yhden mikrosekunnin tarkkuus ja tarkkuus, mikä tarkoittaa, että se pitää sekuntien murto-osan jopa kuuteen numeroon.

Mitä tietokantaa Ruby on Rails käyttää?

Ruby on Rails antaa kehittäjille mahdollisuuden käyttää suurinta osaa suosituista tietokannoista. Oletuksena Ruby on Rails käyttää ActiveRecord-kirjastoa, joka tukee DB2, Firebird, FrontBase, MySQL, OpenBase, Oracle, PostgreSQL, SQLite, Microsoft SQL Server ja Sybase.

Estetiikka ja havainnointi - Kuinka lähestyä käyttökokemuksia

Ux-Suunnittelu

Estetiikka ja havainnointi - Kuinka lähestyä käyttökokemuksia
Tietorakenteen periaatteet mobiililaitteille (infografiikan kanssa)

Tietorakenteen periaatteet mobiililaitteille (infografiikan kanssa)

Ux-Suunnittelu

Suosittu Viestiä
Init.js: Opas Full-Stack-JavaScriptin miksi ja miten
Init.js: Opas Full-Stack-JavaScriptin miksi ja miten
Taso ylöspäin - opas pelin käyttöliittymään (infografiikan kanssa)
Taso ylöspäin - opas pelin käyttöliittymään (infografiikan kanssa)
Tarvitset sankarin: projektipäällikkö
Tarvitset sankarin: projektipäällikkö
Viimeinen opas päivämäärän ja ajan manipulointiin
Viimeinen opas päivämäärän ja ajan manipulointiin
Kuinka poseeraa kuvissa, jotta ne näyttävät rennolta ja luonnolliselta
Kuinka poseeraa kuvissa, jotta ne näyttävät rennolta ja luonnolliselta
 
Käytännöllinen lähestymistapa pelisuunnitteluun
Käytännöllinen lähestymistapa pelisuunnitteluun
Kuinka rakentaa vahva etätyökulttuuri: Haastattelu Christy Schumannin kanssa
Kuinka rakentaa vahva etätyökulttuuri: Haastattelu Christy Schumannin kanssa
Kannustaminen toimintaan ja oikea-aikaisuuteen etätyössä
Kannustaminen toimintaan ja oikea-aikaisuuteen etätyössä
Kuinka rekrytoida UX-tutkimuksen osallistujia
Kuinka rekrytoida UX-tutkimuksen osallistujia
Monimutkainen mutta kiinteä: Katsaus kiinteistöjen vesiputouksiin
Monimutkainen mutta kiinteä: Katsaus kiinteistöjen vesiputouksiin
Luokat
Tuotteen ElinkaariTeknologiaUi-SuunnitteluProsessi Ja TyökalutKaukosäätimen NousuiOS-vinkkejäSuunnitteluKetterä KykyOngelmien karttoittaminenSuunnittelu Ja Ennustaminen

© 2023 | Kaikki Oikeudet Pidätetään

socialgekon.com