Power Platformin työkaluilla voi toteuttaa hyvin monenlaisia ratkaisuja. Mutta jos ratkaisulla on samanaikaisuuden hallintaan liittyviä vaatimuksia, kannattaa pysähtyä miettimään hetkeksi.

Lähtökohtaisesti kaikki toimii, kuten olettaa saattaa.

  • Jos usea käyttäjää muokkaa Canvas Power Appsilla samaa Dataversen riviä, viimeisenä tallennuksen suorittanut voittaa ja jyrää alleen muiden muutokset
  • Yhdestä flow’sta voi olla samanaikaisesti suorituksessa usempi ilmentymä (instance). Mikäli ilmentyvät päivittävät samoja rivejä, viimeinen päivitys jää voimaan

Flow’n kanssa tämä voi olla ongelma. Flow’n rinnakkaiset suoritukset voivat lukea ja kirjoittaa samoja tietueita ristiin, jolloin lopputulos saattaa olla aivan muuta kuin pitäisi.

Katsotaan seuraavaksi voiko tälle tehdä jotain.

Flow’n rinnakkaisuuden hallinta (concurrency control)

Meillä on flow, jonka suorittaminen kestää noin 15 sekuntia.

Flow’sta voi olla käynnissä useampi suoritus samaan aikaan. Yleensä tässä ei ole mitään ongelnaa.

Mutta tällä kertaa on. Haluamme estää flow’n rinnakkaiset suoritukset. Flow’sta voi olla samanaikaisesti käynnissä siis ainoastaan yksi suoritus.

Tämä onnistuu helposti flow’n käynnistävän toiminnon (trigger) asetuksista.

Laitetaan rinnakkaisuuden kontrolli (Concurrency Control) päälle ja asetetaan rinnakkain ajettavien flow’den lukumääräksi 1.

Huom! Asetusta ei voi tallennuksen jälkeen palauttaa alkuperäiseen arvoonsa (Concurrency Controll = Off).

Nyt jos flow käynnistyy useasti lähes samaan aikaan, käynnistyy ainoastaan ensimmäinen pyyntö. Loput odottavat vuoroaan.

Aikanaan kaikki pääsevät (yksi kerrallaan saapumisjärjestyksessä) suoritukseen.

Näppärää.

Huom! Tämä on myös yksi tapa yrittää välttää ettei pyyntöjä mudostu liikaa 5min aikaikkunaan, jolloin pyyntöjä aletaan rajoittamaan (throttling). Tarpeen lähinnä jos flow’ssa suoritetaan samaa yhteyttä käyttäen paljon toimenpiteitä.

Flow’n rinnakkaisuuden hallinta ja Power Apps

Olemme onnistuneesti estäneet flow’n rinnakkaiset suoritukset. Käynnistetään sama flow Power Appsista käsin. Jäämme sovelluksessa odotamaan flow’lta paluuarvoa.

Set(gblReturnValue, Flowwithconcurrencycontrolontrigger.Run())

Hieman muokattu flow’mme näyttää tältä. Käynnistimessä on asetettu rinnakkaisuuden hallinta päälle.

Flow’n tallennus ei kuitenkaan enää onnistu vaan antaa virheen.

Request to XRM API failed with error: ’Message: Flow client error returned with status code ”BadRequest” and details ”{”error”:{”code”:”InvalidFlow”,”message”:”The concurrency configuration of workflow trigger ’manual’ of type ’Request’ at line ’1’ and column ’699’ is not valid. The concurrency control is not supported when the workflow contains actions of type ’response’ without the operationOptions flag set to ’asynchronous’.”}}”. Code: 0x80060467 InnerError: ’.

Koska olemme asettanneet rinnakaisuudenhallinnan päälle, tulee Power Appsille vastauksen lähettävä toiminto (Respond to a Power App or flow) asettaa ei-reaaliaikaiseksi (Asynchronous).

Flow’n asynkroninen paluuarvo

Mitä tämä käytännössä tarkoittaa?

Flow’n käynnistyessä se ei jää odottamaan suorituksen loppumista, vaan palauttaa Power Appsille samantien tiedon että suoritus on otettu vastaan (202 Accepted).

Power Appsissa tämä näkyy siten, että flow’n suoritus näyttää päättyneen, mutta muuttujaan tallentamamme paluuarvo on tyhjä!

Emme siis saakkaan Power Appsiimme paluuarvoa, sillä viestin saapumisen aikaan flow’n suoritus on vielä todellisuudessa kesken.

Avataan Power Appsin monitorointityökalu, josta näemme mitä flow todellisuudessa Power Appsille palauttaa. Paluuarvoa ei viestistä löydy. Mutta näemme että viestin status on 202.

Lisäksi viesti sisältää location-osoitteen. Sen avulla voi seurata pyynnön etenemistä.

Kun flown suoritus on päättynyt, löytyy location-osoitteen takaa lopputulos.

Emme kuitenkaan saa luettua location-osoitetta Power Appsissa… Muutoin voisimme luoda ajastimem (timer), joka säännöllisesti tarkistaa location-osoitteen takaa, onko flow’n suoritus päättynyt ja mikä sen palauttama arvo on.

Pitää keksiä muuta.

Oman lukon toteuttaminen?

Lähestytään ongelmaa toisesta suunnasta. Luodaan flow, jonka rinnakkaisuutta ei hallita (= voimme palauttaa lopputuloksen Power Appsille normaaliin tapaan).

Tällä kertaa estämme flow’n rinnakkaiset suoritukset itse. Oman lukon avulla.

  • Luodaan dataverseen oma lukkotaulu, jossa on yksi rivi
  • Flow’n suorituksen alussa ollaan silmukassa (Do Until), kunnes lukkotaulun rivin arvo on ”unlocked
  • Kun lukko on vapautunut, asetetaan sen arvoksi ”locked” ja suoritetaan varsinaiset toimenpiteet
  • Lopuksi vapautetaan lukko (asetetaan arvoksi ”unlocked”) ja palautetaan flow’n lopputulos Power Appsille

Näppärää mutta…

Vaikka lukon arvoa tutkittaisiin sekunnin välein, on mahdollista että useampi suoritus pääsee silmukasta läpi.

Juu ei.

Muita ideoita?

Jotain pitäisi keksiä.

Entä jos käyttäisimme Respond to a PowerApp or flow -toiminnon sijasta Response -toimintoa?

Luonnollisesti myös Response -toiminto tulee asettaa ei-reaaliaikaiseksi. Eli sama lopputulos.

Onko tämä flow’n rajoitus? Entä jos toteuttaisimme saman Logic Appsina?

Sama alustahan tämä on ja sama haaste taustalla. Eli sama lopputulos.

Entä jos tallennamme flow’n palauttaman location-osoitteen Dataverseen? Power Apps voisi lukea sen sieltä ja seurata sen avulla onko suoritus valmis ja mikä sen lopputulos on?

Mutta… Ei sitä location-osoitetta saa kaivettua käsiinsä flow’llakaan.

Voimme tietenkin tallentaa flow’n lopputuloksen itse Dataverseen.

Näin voimme käynnistää flow’n Power Appsissa ja tämän jälkeen päivystää ajastimen avulla milloin paluuarvo ilmestyy Dataverseen tätä tarkoitusta varten luomaamme tauluun. Flow’lle tulee luonnollisesti välittää parametrina uniikki avain, jolla kyseisen suorituksen lopputulos löydetään taulun rivien joukosta.

Vähän tämä nyt tuntuu hankalalta.

Ratkaisu – Alityönkulku (child flow)

Ratkaisu on kuitenkin lopulta yksinkertainen.

Luodaan uusi flow, jota kutsutaan Power Appsista käsin. Tämän rinnakkaisuusasetukseen ei kosketa. Flow ei tee muuta kuin käynnistää alkuperäisen flow’n (jota suoritetaan yksi kerrallaan). Lopuksi flow palauttaa alityönkululta saamansa arvon Power Appsille.

Flow huolehtii puolestamme paluuarvon saamisen alityönkululta. Se odottaa kärsivällisesti, kunnnes alityönkulku on suoritettu. Vaikka niitä olisi useampi jonossa.

Flow’ta voi kutsua Power Appsista samanaikaisesti ja ne lähtevät heti suoritukseen.

Mutta flow’n kutsumaa aliflowta (jossa varsinainen työ tehdään), suoritetaan yksi kerrallaan

Aliflown palauttama lopptulos palautuu flown kautta PowerAppsille. Juuri kuten halusimmekin.

Yhteenveto

Pääsimme lopulta helpolla. Ratkaisussa on kuitenkin muutama alustan mukanaan tuoma rajoitus, jotka on hyvä tiedostaa.

Yhteydet

Aliflow’n toiminnoissa ei voi normaaliin tapaan käyttää Power Appsin käyttäjän omia yhteyksiä (connection). Kaikki toiminnot tehdään flow’ssa alunperin määritellyillä yhteyksillä. Tämä voi joissain tilanteissa olla ongelma.

Power Appsin ja flown välinen aikakatkaisu (timeout)

Power Appsin ja flow’n välinen yhteys katkeaa, mikäli vastausta ei ole saatu 120 sekunnin kuluessa. Jos aliflown suoritus kestää 10 sekuntia, voi jonossa olla ainoastaan 12 suoritusta. Aliflow kyllä suoritetaan loppuun, mutta Power Apps ei saa koskaan tietää mikä sen lopputulos oli.

Tämä voi olla merkittävä ongelma. Alkuperäinen vaatimuksemme oli, että haluamme lopuksi kertoa Power Appsin käyttäjälle flow’n palauttaman lopputuloksen.

Aliflown kutsun aikakatkaisu (timeout)

Mikäli flow joutuu odottamaa pitkään vuoroaan alityönkulun suhteen, päätyy toiminto (aliflown kutsu) aikakatkaisuun ja uudelleen yrityksiin.

Mikäli flow joutuu odottamaan liian pitkään (=liian monta uudelleenyritystä) ei aliflowta suoriteta ollenkaan, vaan sen kutsu menee virheeseen.

Tässä kohtaa olemme menettäneet yhteyden myös flow’n ja Power Appsin väliltä.

Mikäli tähän tilanteeseen haluaa varautua, on molemmissa flow’ssa tallennettava tilatietoa flow’n mahdollisista virheistä ja lopputuloksesta omaan tauluunsa.

Mikäli yhteys Power Appsin ja flown välillä aikakatkeaa, voidaan Power Appsissa ryhtyä seuraamaan kyseisestä taulusta flow’n lopputulosta (onnistuneen suorituksen lopputulos, tapahtui virhe, flowta ei suoritettu ollenkaan) ja kertoa se käyttäjälle.

Ja näin tämä meni lopulta taas monimutkaiseksi.