Avoimen lähdekoodin DataHub: LinkedInin Metadata Search and Discovery Platform

Avoimen lähdekoodin DataHub: LinkedInin Metadata Search and Discovery Platform

Tarvitsemasi tiedon nopea löytäminen on välttämätöntä jokaiselle yritykselle, joka luottaa suuriin tietomääriin tehdäkseen tietopohjaisia ​​päätöksiä. Tämä ei vaikuta pelkästään datakäyttäjien (mukaan lukien analyytikot, koneoppimisen kehittäjät, datatieteilijät ja tietoinsinöörit) tuottavuuteen, vaan sillä on myös suora vaikutus lopputuotteisiin, jotka ovat riippuvaisia ​​laadukkaasta koneoppimisprosessista (ML). Lisäksi suuntaus kohti koneoppimisalustojen käyttöönottoa tai rakentamista herättää luonnollisesti kysymyksen: mikä on menetelmäsi löytää sisäisesti ominaisuuksia, malleja, mittareita, tietojoukkoja jne.

Tässä artikkelissa puhumme siitä, kuinka julkaisimme tietolähteen avoimella lisenssillä DataHub metatietojen haku- ja etsintäalustallamme projektin alkuajoista lähtien Missä Miten. LinkedIn ylläpitää omaa versiotaan DataHubista erikseen avoimen lähdekoodin versiosta. Aloitamme selittämällä, miksi tarvitsemme kaksi erillistä kehitysympäristöä, keskustelemme sitten avoimen lähdekoodin WhereHowsin käytön aikaisista lähestymistavoista ja vertaamme sisäistä (tuotanto) DataHubin versiota GitHub. Jaamme myös tietoja uudesta automaattisesta ratkaisustamme avoimen lähdekoodin päivitysten lähettämiseen ja vastaanottamiseen, jotta molemmat tietovarastot pysyvät synkronoituina. Lopuksi annamme ohjeet avoimen lähdekoodin DataHubin käytön aloittamiseen ja keskustelemme lyhyesti sen arkkitehtuurista.

Avoimen lähdekoodin DataHub: LinkedInin Metadata Search and Discovery Platform

WhereHows on nyt DataHub!

LinkedInin metatietotiimi aiemmin esitelty DataHub (WhereHowsin seuraaja), LinkedInin haku- ja metatietojen etsintäalusta ja yhteiset suunnitelmat sen avaamiseksi. Pian tämän ilmoituksen jälkeen julkaisimme DataHubista alfaversion ja jaoimme sen yhteisön kanssa. Siitä lähtien olemme jatkuvasti lisänneet tietovarastoa ja työskennelleet kiinnostuneiden käyttäjien kanssa halutuimpien ominaisuuksien lisäämiseksi ja ongelmien ratkaisemiseksi. Meillä on nyt ilo ilmoittaa virallisesta julkaisusta DataHub GitHubissa.

Avoimen lähdekoodin lähestymistavat

WhereHows, LinkedInin alkuperäinen tiedonhakuportaali ja mistä se tulee, alkoi sisäisenä projektina; metatietotiimi avasi sen lähdekoodi vuonna 2016. Siitä lähtien tiimi on aina ylläpitänyt kahta erilaista koodipohjaa – yhtä avoimeen lähdekoodiin ja toista LinkedInin sisäiseen käyttöön – koska kaikki LinkedInin käyttötapauksiin kehitetyt tuoteominaisuudet eivät olleet yleisesti soveltuvia laajemmalle yleisölle. Lisäksi WhereHowsilla on sisäisiä riippuvuuksia (infrastruktuuri, kirjastot jne.), jotka eivät ole avoimen lähdekoodin. Seuraavina vuosina WhereHows kävi läpi monia iteraatioita ja kehityssyklejä, mikä teki kahden koodikannan synkronoinnista suuren haasteen. Metadatatiimi on vuosien varrella kokeillut erilaisia ​​lähestymistapoja yrittääkseen pitää sisäisen ja avoimen lähdekoodin kehityksen synkronoituna.

Ensimmäinen yritys: "Avoin lähdekoodi ensin"

Noudatimme alun perin "avoimen lähdekoodin etusijalle" -kehitysmallia, jossa suurin osa kehityksestä tapahtuu avoimen lähdekoodin arkistossa ja muutoksia tehdään sisäistä käyttöönottoa varten. Tämän lähestymistavan ongelmana on, että koodi työnnetään aina ensin GitHubiin ennen kuin se on täysin tarkistettu sisäisesti. Ennen kuin avoimen lähdekoodin arkistosta on tehty muutoksia ja uusi sisäinen käyttöönotto on tehty, emme löydä tuotantoongelmia. Huonon käyttöönoton tapauksessa syyllisen määrittäminen oli myös erittäin vaikeaa, koska muutoksia tehtiin erissä.

Lisäksi tämä malli heikensi tiimin tuottavuutta kehitettäessä uusia ominaisuuksia, jotka vaativat nopeita iteraatioita, koska se pakotti kaikki muutokset ensin työntämään avoimen lähdekoodin arkistoon ja sitten työntämään sisäiseen arkistoon. Käsittelyajan lyhentämiseksi tarvittava korjaus tai muutos voitiin tehdä ensin sisäisessä arkistossa, mutta tästä tuli valtava ongelma, kun nämä muutokset yhdistettiin takaisin avoimen lähdekoodin arkistoon, koska nämä kaksi arkistoa eivät olleet synkronoituja.

Tämä malli on paljon helpompi toteuttaa jaetuissa alustoissa, kirjastoissa tai infrastruktuuriprojekteissa kuin täysin varustetuissa mukautetuissa verkkosovelluksissa. Lisäksi tämä malli on ihanteellinen projekteihin, jotka alkavat avoimen lähdekoodin alusta alkaen, mutta WhereHows rakennettiin täysin sisäiseksi verkkosovellukseksi. Kaikkien sisäisten riippuvuuksien poistaminen oli todella vaikeaa, joten meidän piti säilyttää sisäinen haarukka, mutta sisäisen haarukan säilyttäminen ja enimmäkseen avoimen lähdekoodin kehittäminen ei oikein toiminut.

Toinen yritys: "Sisäinen ensin"

**Toisena yrityksenä siirryimme "sisäiseen ensin" -kehitysmalliin, jossa suurin osa kehityksestä tapahtuu talon sisällä ja avoimeen lähdekoodiin tehdään säännöllisesti muutoksia. Vaikka tämä malli sopii parhaiten meidän käyttötapaukseemme, siinä on luontaisia ​​ongelmia. Kaikkien eroavaisuuksien siirtäminen suoraan avoimen lähdekoodin arkistoon ja yhdistämisristiriitojen yrittäminen myöhemmin on vaihtoehto, mutta se vie aikaa. Kehittäjät yrittävät useimmissa tapauksissa olla tekemättä tätä joka kerta, kun he tarkistavat koodinsa. Tämän seurauksena tämä tehdään paljon harvemmin, erissä, mikä vaikeuttaa yhdistämisristiriitojen ratkaisemista myöhemmin.

Kolmannella kerralla onnistui!

Yllä mainitut kaksi epäonnistunutta yritystä johtivat WhereHows GitHub -tietovaraston vanhentumiseen pitkään. Tiimi jatkoi tuotteen ominaisuuksien ja arkkitehtuurin parantamista, jotta sisäinen WhereHows for LinkedIn -versio muuttui edistyneemmäksi kuin avoimen lähdekoodin versio. Sillä oli jopa uusi nimi - DataHub. Aiempien epäonnistuneiden yritysten perusteella tiimi päätti kehittää skaalautuvan, pitkän aikavälin ratkaisun.

Kaikissa uusissa avoimen lähdekoodin projekteissa LinkedInin avoimen lähdekoodin tiimi neuvoo ja tukee kehitysmallia, jossa projektin moduulit kehitetään kokonaan avoimessa lähdekoodissa. Versioidut artefaktit otetaan käyttöön julkisessa arkistossa ja tarkistetaan sitten takaisin sisäiseen LinkedIn-artefaktiin käyttämällä ulkoisen kirjaston pyyntö (ELR). Tämän kehitysmallin noudattaminen ei ole hyvä vain avoimen lähdekoodin käyttäjille, vaan se johtaa myös modulaarisempaan, laajennettavampaan ja liitettävissä olevaan arkkitehtuuriin.

Kypsä taustasovellus, kuten DataHub, vaatii kuitenkin huomattavasti aikaa saavuttaakseen tämän tilan. Tämä sulkee pois myös mahdollisuuden avoimeen lähdekoodiin täysin toimivaan toteutukseen ennen kuin kaikki sisäiset riippuvuudet on poistettu kokonaan. Siksi olemme kehittäneet työkaluja, joiden avulla voimme tehdä avoimen lähdekoodin sisällön nopeammin ja paljon pienemmällä kivulla. Tämä ratkaisu hyödyttää sekä metatietotiimiä (DataHub-kehittäjä) että avoimen lähdekoodin yhteisöä. Seuraavissa osissa käsitellään tätä uutta lähestymistapaa.

Avoimen lähdekoodin julkaisuautomaatio

Metadata-tiimin uusin lähestymistapa avoimen lähdekoodin DataHubiin on kehittää työkalu, joka synkronoi automaattisesti sisäisen koodikannan ja avoimen lähdekoodin arkiston. Tämän työkalupakin korkeatasoisia ominaisuuksia ovat:

  1. Synkronoi LinkedIn-koodi avoimeen lähdekoodiin tai vastaavasta rsync.
  2. Lisenssiotsikon luominen, samanlainen kuin Apache-rotta.
  3. Luo automaattisesti avoimen lähdekoodin toimituslokeja sisäisistä toimituslokeista.
  4. Estä sisäiset muutokset, jotka rikkovat avoimen lähdekoodin koontiversiot riippuvuustesti.

Seuraavissa alaosissa perehdytään yllä mainittuihin toimintoihin, joissa on mielenkiintoisia ongelmia.

Lähdekoodin synkronointi

Toisin kuin DataHubin avoimen lähdekoodin versio, joka on yksi GitHub-tietovarasto, DataHubin LinkedIn-versio on useiden arkiston yhdistelmä (kutsutaan sisäisesti useita tuotteita). DataHub-käyttöliittymä, metatietomallikirjasto, metatietovaraston taustapalvelu ja suoratoistotyöt sijaitsevat erillisissä arkistoissa LinkedInissä. Avoimen lähdekoodin käyttäjien toiminnan helpottamiseksi meillä on kuitenkin yksi tietovarasto DataHubin avoimen lähdekoodin versiota varten.

Avoimen lähdekoodin DataHub: LinkedInin Metadata Search and Discovery Platform

Kuva 1: Synkronointi arkistojen välillä LinkedIn DataHub ja yksi arkiston DataHub avoin lähdekoodi

Uusi työkalumme luo automaattisesti kutakin lähdetiedostoa vastaavan tiedostotason kartoituksen automaattisen koonti-, push- ja pull-työnkulkujen tukemiseksi. Työkalupakkaus vaatii kuitenkin alkumäärityksen, ja käyttäjien on toimitettava korkean tason moduulikartoitus alla olevan kuvan mukaisesti.

{
  "datahub-dao": [
    "${datahub-frontend}/datahub-dao"
  ],
  "gms/impl": [
    "${dataset-gms}/impl",
    "${user-gms}/impl"
  ],
  "metadata-dao": [
    "${metadata-models}/metadata-dao"
  ],
  "metadata-builders": [
    "${metadata-models}/metadata-builders"
  ]
}

Moduulitason kartoitus on yksinkertainen JSON, jonka avaimet ovat avoimen lähdekoodin tietovaraston kohdemoduulit ja arvot ovat LinkedIn-arkiston lähdemoduulien luettelo. Mitä tahansa avoimen lähdekoodin arkiston kohdemoduulia voidaan syöttää millä tahansa määrällä lähdemoduuleita. Käytä lähdemoduulien tietovarastojen sisäiset nimet ilmoittamiseksi merkkijonon interpolointi bash-tyyliin. Moduulitason kartoitustiedoston avulla työkalut luovat tiedostotason kartoitustiedoston tarkistamalla kaikki tiedostot niihin liittyvissä hakemistoissa.

{
  "${metadata-models}/metadata-builders/src/main/java/com/linkedin/Foo.java":
"metadata-builders/src/main/java/com/linkedin/Foo.java",
  "${metadata-models}/metadata-builders/src/main/java/com/linkedin/Bar.java":
"metadata-builders/src/main/java/com/linkedin/Bar.java",
  "${metadata-models}/metadata-builders/build.gradle": null,
}

Tiedostotason kartoitus luodaan automaattisesti työkaluilla; käyttäjä voi kuitenkin päivittää sen myös manuaalisesti. Tämä on LinkedIn-lähdetiedoston 1:1-kartoitus avoimen lähdekoodin arkistossa olevaan tiedostoon. Tähän tiedostoliitosten automaattiseen luomiseen liittyy useita sääntöjä:

  • Jos kohdemoduulille avoimessa lähdekoodissa on useita lähdemoduuleja, voi syntyä ristiriitoja, esim. FQCN, joka on olemassa useammassa kuin yhdessä lähdemoduulissa. Konfliktinratkaisustrategiana työkalumme ovat oletuksena "viimeinen voittaa" -vaihtoehto.
  • "null" tarkoittaa, että lähdetiedosto ei ole osa avoimen lähdekoodin arkistoa.
  • Jokaisen avoimen lähdekoodin lähettämisen tai purkamisen jälkeen tämä kartoitus päivitetään automaattisesti ja siitä luodaan tilannevedos. Tämä on tarpeen lähdekoodin lisäysten ja poistojen tunnistamiseksi viimeisen toiminnon jälkeen.

Toimituslokien luominen

Toimituslokit avoimen lähdekoodin toimituksille luodaan myös automaattisesti yhdistämällä sisäisten tietovarastojen toimituslokit. Alla on esimerkkitoimitusloki, joka näyttää työkalumme luoman toimituslokin rakenteen. Toimitus osoittaa selvästi, mitkä lähdetietovarastojen versiot on pakattu kyseiseen toimitukseen, ja tarjoaa yhteenvedon toimituslokista. Tarkista tämä tehdä käyttämällä todellista esimerkkiä työkalupakkimme luomasta toimituslokista.

metadata-models 29.0.0 -> 30.0.0
    Added aspect model foo
    Fixed issue bar

dataset-gms 2.3.0 -> 2.3.4
    Added rest.li API to serve foo aspect

MP_VERSION=dataset-gms:2.3.4
MP_VERSION=metadata-models:30.0.0

Riippuvuustestaus

LinkedInissä on riippuvuustestauksen infrastruktuuri, joka auttaa varmistamaan, että sisäiseen monituotteeseen tehdyt muutokset eivät katkaise riippuvien monituotteiden kokoonpanoa. Avoimen lähdekoodin DataHub-arkisto ei ole monituote, eikä se voi olla suora riippuvuus mistään useista tuotteista, mutta avoimen lähdekoodin DataHub-lähdekoodin hakevan usean tuotteen kääreen avulla voimme silti käyttää tätä riippuvuustestausta. Siten mikä tahansa muutos (joka saattaa myöhemmin tulla esille) missä tahansa avoimen lähdekoodin DataHub-tietovarastoa syöttävässä monituotteessa laukaisee koontitapahtuman shell-monituotteessa. Siksi kaikki muutokset, jotka eivät pysty rakentamaan kääretuotetta, läpäisevät testit ennen alkuperäisen tuotteen sitomista, ja ne palautetaan.

Tämä on hyödyllinen mekanismi, joka auttaa estämään sisäisen sitoumuksen, joka rikkoo avoimen lähdekoodin koontiversion ja havaitsee sen toimitushetkellä. Ilman tätä olisi melko vaikeaa määrittää, mikä sisäinen vahvistus aiheutti avoimen lähdekoodin arkiston koontiversion epäonnistumisen, koska keräämme sisäiset muutokset DataHubin avoimen lähdekoodin arkistoon.

Erot avoimen lähdekoodin DataHubin ja tuotantoversiomme välillä

Tähän mennessä olemme keskustelleet ratkaisustamme kahden DataHub-tietovaraston version synkronoimiseksi, mutta emme ole vieläkään hahmottaneet syitä, miksi tarvitsemme alun perin kahta erilaista kehitysvirtaa. Tässä osiossa luetellaan DataHubin julkisen version ja LinkedIn-palvelimien tuotantoversion väliset erot ja selitetään näiden erojen syyt.

Yksi ristiriitaisuuksien lähde johtuu siitä, että tuotantoversiossamme on riippuvuuksia koodista, joka ei ole vielä avointa lähdekoodia, kuten LinkedInin Offspring (LinkedInin sisäinen riippuvuuden lisäyskehys). Jälkiä käytetään laajasti sisäisissä koodikannoissa, koska se on suositeltu menetelmä dynaamisen konfiguroinnin hallintaan. Mutta se ei ole avoimen lähdekoodin; joten meidän piti löytää avoimen lähdekoodin vaihtoehtoja avoimen lähdekoodin DataHubille.

Muitakin syitä on. Kun luomme laajennuksia metatietomalliin LinkedInin tarpeisiin, nämä laajennukset ovat yleensä hyvin LinkedIn-kohtaisia ​​eivätkä välttämättä koske suoraan muita ympäristöjä. Meillä on esimerkiksi erittäin tarkat tunnisteet osallistujatunnuksille ja muun tyyppisille vastaaville metatiedoille. Joten olemme nyt jättäneet nämä laajennukset pois DataHubin avoimen lähdekoodin metatietomallista. Kun olemme tekemisissä yhteisön kanssa ja ymmärrämme heidän tarpeitaan, työskentelemme tarvittaessa näiden laajennusten yleisten avoimen lähdekoodin versioiden parissa.

Helppokäyttöisyys ja helpompi mukauttaminen avoimen lähdekoodin yhteisöön inspiroivat myös joitain eroja DataHubin kahden version välillä. Erot virrankäsittelyinfrastruktuurissa ovat hyvä esimerkki tästä. Vaikka sisäinen versiomme käyttää hallittua stream-käsittelykehystä, päätimme käyttää avoimen lähdekoodin versiossa sisäänrakennettua (erillistä) stream-käsittelyä, koska se välttää uuden infrastruktuuririippuvuuden luomisen.

Toinen esimerkki erosta on yksi GMS (Generalized Metadata Store) avoimen lähdekoodin toteutuksessa useiden GMS:ien sijaan. GMA (Generalized Metadata Architecture) on DataHubin tausta-arkkitehtuurin nimi, ja GMS on metatietovarasto GMA:n yhteydessä. GMA on erittäin joustava arkkitehtuuri, jonka avulla voit jakaa jokaisen tietorakenteen (esim. tietojoukot, käyttäjät jne.) omaan metatietosäilöön tai tallentaa useita tietorakenteita yhteen metatietosäilöön niin kauan kuin rekisteri sisältää tietorakennekartoituksen GMS on päivitetty. Käytön helpottamiseksi valitsimme yhden GMS-instanssin, joka tallentaa kaikki erilaiset tietorakenteet avoimen lähdekoodin DataHubiin.

Täydellinen luettelo näiden kahden toteutuksen eroista on alla olevassa taulukossa.

Ominaisuudet
LinkedIn DataHub
Avoimen lähdekoodin DataHub

Tuetut tietorakenteet
1) Tietojoukot 2) Käyttäjät 3) Mittarit 4) ML-ominaisuudet 5) Kaaviot 6) Kojelaudat
1) Tietojoukot 2) Käyttäjät

Tuetut metatietolähteet tietojoukoille
1) Ambry 2) sohvaalusta 3) Dalidit 4) Espresso 5) HDFS 6) Hive 7) Kafka 8) MongoDB 9) MySQL 10) Oracle 11) Pinot 12) Presto 12) Seas 13) Teradata 13) Vector 14) Venetsia
Hive Kafka RDBMS

Pub-sub
LinkedIn Kafka
Yhtenäinen Kafka

Suoratoiston käsittely
Ylläpito
Upotettu (erillinen)

Riippuvuuden lisäys ja dynaaminen konfigurointi
LinkedIn-jälkeläiset
Kevät

Rakenna työkalut
Ligradle (LinkedInin sisäinen Gradle-kääre)
Gradlew

CI / CD
CRT (LinkedInin sisäinen CI/CD)
TravisCI ja Docker-napa

Metatietokaupat
Hajautettu useita GMS: 1) Dataset GMS 2) Käyttäjä GMS 3) Metric GMS 4) Ominaisuus GMS 5) Kaavio/kojelauta GMS
Yksittäinen GMS: 1) tietojoukoille 2) käyttäjille

Mikropalvelut Docker-konteissa

Satamatyöläinen yksinkertaistaa sovellusten käyttöönottoa ja jakelua konteissa. DataHubin palvelun jokainen osa on avoimen lähdekoodin, mukaan lukien infrastruktuurikomponentit, kuten Kafka, Elasticsearch, ollut Neo4j и MySQL, on oma Docker-kuvansa. Käytimme Docker-konttien järjestämiseen Docker Compose.

Avoimen lähdekoodin DataHub: LinkedInin Metadata Search and Discovery Platform

Kuva 2: Arkkitehtuuri DataHub *avoin lähdekoodi**

Näet DataHubin korkean tason arkkitehtuurin yllä olevassa kuvassa. Infrastruktuurikomponenttien lisäksi siinä on neljä erilaista Docker-konttia:

datahub-gms: metatietojen tallennuspalvelu

datahub-frontend: sovellus Pelaa, joka palvelee DataHub-liitäntää.

datahub-mce-consumer: sovellus Kafka-virrat, joka käyttää metatietojen muutostapahtuman (MCE) virtaa ja päivittää metatietosäilön.

datahub-mae-consumer: sovellus Kafka-virrat, joka käyttää metatietojen tarkastustapahtumavirtaa (MAE) ja luo hakuindeksin ja kaaviotietokannan.

Avoimen lähdekoodin arkiston dokumentaatio ja alkuperäinen DataHub-blogiviesti sisältää tarkempaa tietoa eri palvelujen toiminnoista.

DataHubin CI/CD on avoimen lähdekoodin

Avoimen lähdekoodin DataHub-arkisto käyttää TravisCI jatkuvaan integrointiin ja Docker-napa jatkuvaa käyttöönottoa varten. Molemmissa on hyvä GitHub-integraatio ja ne on helppo asentaa. Useimmille yhteisön tai yksityisten yritysten kehittämille avoimen lähdekoodin infrastruktuurille (esim. yhtyviä), Docker-kuvat luodaan ja otetaan käyttöön Docker Hubiin yhteisön helpottamiseksi. Mitä tahansa Docker Hubista löytyvää Docker-kuvaa voidaan käyttää helposti yksinkertaisella komennolla telakkaveto.

Jokaisella DataHubin avoimen lähdekoodin arkistoon tehdyllä sitoumuksella kaikki Docker-kuvat luodaan automaattisesti ja otetaan käyttöön Docker Hubiin "uusimmalla" -tunnisteella. Jos Docker Hub on määritetty joidenkin säännöllisten lausekkeiden haarojen nimeäminen, kaikki avoimen lähdekoodin arkiston tagit julkaistaan ​​myös vastaavilla tunnisteiden nimillä Docker Hubissa.

DataHubin käyttäminen

DataHubin määrittäminen on hyvin yksinkertainen ja koostuu kolmesta yksinkertaisesta vaiheesta:

  1. Kloonaa avoimen lähdekoodin arkisto ja suorita kaikki Docker-säilöt docker-compose-toiminnolla käyttämällä mukana toimitettua Docker-compose-skriptiä nopeaa alkua varten.
  2. Lataa arkistossa olevat näytetiedot käyttämällä myös mukana tulevaa komentorivityökalua.
  3. Selaa DataHubia selaimessasi.

Aktiivisesti seurattu Gitter chatti myös määritetty pikakysymyksiä varten. Käyttäjät voivat myös luoda ongelmia suoraan GitHub-tietovarastoon. Mikä tärkeintä, otamme mielellämme vastaan ​​kaiken palautteen ja ehdotukset!

Tulevaisuuden suunnitelmat

Tällä hetkellä jokainen avoimen lähdekoodin DataHubin infrastruktuuri tai mikropalvelu on rakennettu Docker-konttiksi, ja koko järjestelmä on ohjattu docker-compose. Koska suosio ja laajalle levinneisyys Kubernetes, haluamme myös tarjota Kubernetes-pohjaisen ratkaisun lähitulevaisuudessa.

Suunnittelemme myös avaimet käteen -ratkaisun DataHubin käyttöönottamiseksi julkisessa pilvipalvelussa, kuten Taivaansininen, AWS tai Google Cloud. Ottaen huomioon äskettäisen ilmoituksen LinkedInin siirtymisestä Azureen, tämä vastaa metatietotiimin sisäisiä prioriteetteja.

Viimeisenä mutta ei vähäisimpänä, kiitos kaikille avoimen lähdekoodin yhteisössä oleville DataHubin varhaisille käyttäjille, jotka ovat arvioineet DataHubin alfat ja auttaneet meitä tunnistamaan ongelmia ja parantamaan dokumentaatiota.

Lähde: will.com

Lisää kommentti