{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/migracja-danych-do-cosmos-db","result":{"data":{"markdownRemark":{"html":"<p>Całkiem niedawno temu zmagałem się z zadaniem polegającym na migracji pewnej ilości danych (przyjmijmy, że setki tysięcy dokumentów) do Cosmos DB, czyli Azure'owej bazy dokumentowej. Okazuje się, że strategia na \"hurrra!\" nie zadziała - jeśli będziemy wrzucać dokumenty jeden po drugim, po prostu przy którymś obrocie pętli, kod się nam wykrzaczy wyjątkiem. Co w takiej sytuacji należy zrobić?</p>\n<h1>Bulk SDK</h1>\n<p>W najprostszym podejściu, mając kolekcję danych (dokumentów) do wrzucenia do Cosmosa, możemy po prostu przeiterować po nich, dla każdego wywołując operację <em>upsert</em>. Łatwo się domyślić, że będzie to jednak niewydajne, zwłaszcza, że każda operacja będzie oddzielnym requestem. Na początku z pomocą przychodzi <a href=\"https://devblogs.microsoft.com/cosmosdb/introducing-bulk-support-in-the-net-sdk/\">\"bulk support\"</a>, wprowadzony w wersji 3.4.0 SDK. Gdy ustawimy <code class=\"language-text\">AllowBulkExecution = true</code> w <code class=\"language-text\">CosmosClientOptions</code> klient (stworzony z użyciem takiego obiektu opcji) będzie miał nową właściwość: będzie kolejkować ileś-tam pojedynczych upsertów w jeden request po sieci do Azure'a. A w kodzie - wystarczy, że odpalimy wszystkie upserty jako taski i poczekamy na wszystkie, jak w poniższym przykładzie:</p>\n<div class=\"gatsby-highlight\" data-language=\"csharp\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-csharp line-numbers\"><code class=\"language-csharp\"><span class=\"token class-name\">List<span class=\"token punctuation\">&lt;</span>Task<span class=\"token punctuation\">></span></span> tasks <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">List<span class=\"token punctuation\">&lt;</span>Task<span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">foreach</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">var</span></span> itemToInsert <span class=\"token keyword\">in</span> itemsToInsert<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    tasks<span class=\"token punctuation\">.</span><span class=\"token function\">Add</span><span class=\"token punctuation\">(</span>container<span class=\"token punctuation\">.</span><span class=\"token function\">UpsertItemAsync</span><span class=\"token punctuation\">(</span>itemToInsert<span class=\"token punctuation\">,</span> partitionKey<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">await</span> Task<span class=\"token punctuation\">.</span><span class=\"token function\">WhenAll</span><span class=\"token punctuation\">(</span>tasks<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>To robi dużą różnicę - cały proces przyspiesza, bo robimy po prostu mniej requestów sieciowych. Jak dokładnie to jest rozdzielane przez SDK i jaka jest pojemność \"bufora\", tego autorzy nie SDK nie zdradzają.\nJednakże taka zamiana z \"dużo mały kroczków\" na \"mało wielkich kroków\" (upraszczam, rzecz jasna) może być niewystarczająca. Niby o tym piszą w dokumentacji, ale nie zwrócłem na to uwagi na początku...</p>\n<h1>Zwiększ throughput</h1>\n<p>Bulk API generuje jednak dodatkowy problem - skoro szybciej pakujemy dane do chmury, to innymi słowy przystawiamy do Azure'a grubszą rurę. Tyle, że Cosmos DB to nie papier i wszystkiego nie przyjmie. Komunikacja z Cosmos DB jest bowiem ograniczona przez ustawiony na instancji throughput. Troughput (przepustowość) to ta magiczna wartość, liczona w jeszcze bardziej magicznej jednostce RU, za którą płacimy - a dokładniej: płacimy za rezerwację danej przepustowości. Troughput może być ustawiony na sztywno albo autoskalować się w pewnym przedziale. W drugiej opcji polega to na tym, że określamy maksymalną przepustowość, a chmura sama skaluje ją pomiędzy 10% a 100&#x26; maksymalnego limitu (dzięki temu w czasie mniejszego obciążenia można sporo zaoszczędzić).\nW skrócie zatem - jeśli odpalimy bulk API bez zmiany przepustowości, to jest bardzo prawdopodobne, że ona się wyczerpie (no chyba, że mamy ustawiony bardzo duży ten górny limit, no ale to ma sens tylko wtedy gdy mamy naprawdę ogromną amplitudę ruchu). Rozwiązaniem jest po prostu czasowe podniesienie tego limitu - ja się nie patyczkowałem i dałem x10. To oczywiście podbije nam koszty, bo w czasie migrowania danych, bulk API będzie starał się pakować, ile się da - a więc przy autoskalowaniu przepustowości na pewno zostanie ona podniesiona w stronę 100%, ale dzięki temu z jednej strony przyspieszymy cały proces, a z drugiej - unikniemy odrzuconych upsertów.\nTrzeba tylko wiedzieć, że sam proces podnoszenia górnego limitu przepustowości może trwać nawet godzinę (sic!) oraz pamiętać, żeby po całej operacji przywrócić wartość przepustowości - w przeciwnym wypadku będzie ona \"wisieć\" na za dużej wartości (przykładowo: jeśli mieliśmy dotychzas górny limit na 4000 RU, to przepustowość skalowała się między 400 a 4000 RU; jeśli zostawimy throughput na tymczasowej wartości np. x10, to zostaniemy na 4000-40000 RU, a więc najniższa wartość będzie taka, jak górny limit normalnego trybu...).</p>","excerpt":"Całkiem niedawno temu zmagałem się z zadaniem polegającym na migracji pewnej ilości danych (przyjmijmy, że setki tysięcy dokumentów) do Cosmos DB, czyli Azure…","frontmatter":{"date":"09 August, 2021","path":"/blog/migracja-danych-do-cosmos-db","title":"Migracja danych do Comos DB"},"fields":{"readingTime":{"text":"3 min read"}}}},"pageContext":{}},"staticQueryHashes":["3649515864","63159454"]}