Azure Functions CosmosDB input bindingsを使う上でのコツ – バインド変数編

さて、今度はバインド変数に関する躓きを共有します。

クエリエクスプローラとFunctions経由との間でクエリの結果が異なる???

例えば以下のようなクエリをCosmosDBに投げるとします。

SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = 30

クエリエクスプローラでは以下のような結果が得られました。

[
    {
        "id": "user-123"
    },
    {
        "id": "user-124"
    },
    {
        "id": "user-133"
    }
]

今度はクエリの30に当たる箇所を{generation}として、sqlQueryに設定します。

SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = {generation}

こんな感じですね。そしてFunctions側でQueue Storageトリガーを使って以下のようなジョブを受け取ります。

{"generation": 30}

この時、Functionsで受け取れるCosmosDB inout bindingsの結果は以下のようなものとなります。

[]

すっからかんですね。。。一体どういうことでしょう。

理由

初めて知った時には非常に驚きましたが、どうやら各bindingsにわたってくる値は文字列としてキャストされてしまうっぽいということです。

本当は以下のように展開してほしいのですが。。。

SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = 30

こんな感じになってしまうということです。

SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = "30"

これは厄介ですね。。。。

対処

これを回避するには、クエリの方で強制的に数値に置き換えてやればよいのですが、CosmosDBのビルトイン関数には数値へのキャストをしてくれるものがありません。

そこで登場するのがユーザ定義関数(User Defined Function/UDF)です。

CosmosDBでは、javascriptを使ってユーザが関数を定義することができるのです。

CosmosDBのブレードにあるスクリプトエクスプローラというメニューから、UDFの定義ができます。

UDF開発画面

上記画像のような関数を定義してやり、sqlQuery側を以下のように変更すると、無事に期待した結果が得られます。

SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = udf.ConvertToNumber({generation})

このほか、時刻にまつわる関数などもCosmosDBにはビルトインでは存在しませんが、このようなUDFを作って対応することが可能です。

CosmosDBのUDFは非常に強力な機能ですので、是非とも有効に活用していきたいものですね。

元ネタ:https://stackoverflow.com/questions/44916811/difference-among-the-azure-cosmosdb-query-explorers-results-and-the-azure-funct/44918767#44918767

Azure Functions CosmosDB input bindingsを使う上でのコツ – 余剰演算子編

先日あたりからAzure FunctionsのCosmosDB input bindingsを使っていて、いくつかはまりどころのような挙動に出くわしましたので、その説明と対処法を紹介していきます。

sqlQueryで剰余演算子(Modulation Symbol)がそのまま使えない

例えば以下のようなQueryをCosmosDBに投げるとします。

SELECT c.id FROM c WHERE c.level % 20

このクエリを直接クエリエクスプローラで実行する場合、期待する結果が得られます。

ところがこのクエリをAzure FunctionsのCosmosDB input bindingsにsqlQueryとして指定した場合、以下のようなエラーがでてしまい、期待した動作をしません。

2017-07-05T16:45:43.113 Exception while executing function: Functions.MyFunction. Microsoft.Azure.WebJobs.Host: The '%' at position 34 does not have a closing '%'.

理由

実はsqlQueryの仕様として、%というのはアプリケーション設定の値を参照するために使う記号でして、%myappSetting%のような感じで使うものだったんですね。

そのため、単独で%が登場すること自体があってはならない、と、そういうことが理由となっています。

対処

対処法の一つとして、アプリケーション設定値に%を設定して、それを使えばよい、というものがあります。

アプリケーション設定から値を設定する

上記画像のように、例えばmodulationsymbolというアプリケーション設定値を定義しておき、sqlQuery側では以下のように%%modulationsymbol%に置き換えることで、期待通りの結果を得ることができます。

SELECT c.id FROM c WHERE c.level %modulationsymbol% 20

あまりカッコよくはないですが、致し方がない感じはしますね。

元ネタ:https://stackoverflow.com/questions/44903632/sqlquery-of-documentdb-input-bindings-with-modulation-symbol-makes-functions-fa

YAPC::Fukuoka 2017 HAKATAでトークしました

YAPC::Fukuoka 2017 HAKATAにてトークしてきました。

PaaSをつかって怠惰・短気・傲慢にシステムを作るためのマインドセットについての話です。当日は徳丸さんの裏番組ということもあり、人が来てくれるのかと心配しておりましたが、思いの外結構人がいらっしゃったので、話した甲斐があったと思っております。

これを機に「Azureってすごいじゃん!」とか「Azure PaaSで作ってみようかな」と思ってもらえれば本望です。

スタッフの皆様、会場提供してくださったLINE様、その他YAPC::Fukuoka成功に向けて尽力された皆様に、改めて御礼申し上げます。ありがとうございました。

YAPC::Fukuoka 2017 Hakataにて登壇することが決まりました

7/1(土)に開催されるYAPC::Fukuoka 2017 Hakataへの登壇が決まりました。

発表タイトルはBe PaaS Monger – クラウドエンジニアの三大美徳、またはIaaSを使わない3つの理由となります。

上記のセッションでは、PaaSを使ったシステム設計・構築におけるマインドセットを、プログラマの三大美徳になぞらえて解説します。

クラウドの運用に人手がかかっていてお悩みの方、PaaSという物がよくわからないという方などに向けた内容となる予定です。

Azure Functions Cosmos DB Bindingsの新機能sqlQueryを試してみた

Cosmos DB Binding

Azure Functions Cosmos DB bindingsというドキュメントが公開されていることに最近気がついたのですが、その中でも個人的に以前から密かに待望していた機能であるsqlQueryについて、とうとう公式に明記されました。

大変便利と思われるこの機能を早速試してみましたので、本エントリで紹介していきます。

なお、本エントリの前提知識として、以下のエントリを読んでおくことをお勧めします。

Azure DocumentDB inputを設定する

今回私が作った関数のfunction.jsonですが、DocumentDB inputの設定は以下のようになっております。よく見ると、sqlQueryに見慣れたSQLが記述されていますね。

これを標準エディタで見てみますと、以下のようになります。

sqlQueryの仕様

テーブル名を必ず指定する必要がある(ただし何でもOK)

気をつけなければいけない点として、フィールド名を指定する場合はテーブル名を指定する必要があるということです。

function.jsonでコレクション名を指定してあるため、テーブル名そのものは何でもOKであり、特に何らかの影響を与えるわけではありません。このため、慣例的にテーブル名をcとすることが多いようです。そのため、フィールド名nameを指定するためにはc.nameとする必要があります。

クエリバインド変数の指定方法

httpバインディングで受け付ける関数の場合、sqlQueryに対して{name}などとすることでクエリバインド変数を利用することが可能です。

例えば SELECT c.id FROM c WHERE c.type={type}というsqlQueryを指定した場合、getパラメータにtype=imageのように指定することで、typeimageであるドキュメントを全て持ってくることができます。

今回使ったデータ

今回の検証のために、あらかじめ別の関数を使ってCosmos DBに以下のようなドキュメントデータをいくつか登録しておきました。

関数のコーディング

普段ですとPerlやBashでコードを書いたりするのですが、今回はPHP7での実装事例を紹介します。

中身はこれしかありませんが、一応おさらいも兼ねて解説していきます。

1行目ですが、このコードはphpで動作するコードですので、Functionと言えど <?phpで開始する必要があります。

2行目ではfunction.jsonで指定したsqlQueryの結果を$messageに格納する処理を行っています。

getenvの引数に指定する文字列は、function.jsonで指定したnameと同じものを指定します。これによりgetenvはCosmos DBから取得したJSONデータを内包した一時ファイルのパスを返します。あとはfile_get_contentsで中身を取り出し、json_decodestdClassに復元してやることで、phpで自在に操作可能となります。

3行目はレスポンス処理を行っています。getenvでレスポンス用の一時ファイルパスを取得し、そこにjson_encodeしたデータをfile_put_contentsで書き込んでやるだけです。

実際にアクセスしてみる

以下は、GETパラメータにname=tagoをつけてリクエストを投げてみた結果です。

任意の絞り込みを行った結果を取得できていることがわかると思います。

まとめ

今回の検証で、Azure Functions + Cosmos DBを使ったSQLライクな問い合わせができることがわかりました。

ちなみにCosmos DBに対するクエリとして、既に一部の集計関数がサポートされています。ただし、GROUP BY句は現時点ではサポートしていません(かなりアツい要望は多数あるようです。私も欲しい)。

Otogiri-0.18 – A lightweight medicine for using database – metacpan.org

Otogiri-0.18 – A lightweight medicine for using database – metacpan.org

Azure DocumentDB が Azure Cosmos DB に名称変更したようです

Azure Functionsと共に利用すると便利なフルマネージドNoSQLサービスである「Azure DocumentDB」が名称変更して「Azure Cosmon DB」になったようです。

以下、Azure Portal上の説明を引用します。

Azure Cosmos DB is a fully managed, globally-distributed, horizontally scalable in storage and throughput, multi-model database service backed up by comprehensive SLAs. Azure Cosmos DB is the next generation of Azure DocumentDB. Cosmos DB was built from the ground up with global distribution and horizontal scale at its core – it offers turn-key global distribution across any number of Azure regions by transparently scaling and replicating your data wherever your users are. You can elastically scale throughput and storage worldwide and pay only for the throughput and storage you need. Cosmos DB guarantees single-digit millisecond latencies at the 99th percentile anywhere in the world, offers multiple well-defined consistency models to fine-tune for performance and guaranteed high availability with multi-homing capabilities – all backed by industry leading service level agreements (SLAs).

Cosmos DB is truly schema-agnostic – it automatically indexes all the data without requiring you to deal with schema and index management. Cosmos DB is multi-model – it natively supports document, key-value, graph and columnar data models. With Cosmos DB, you can access your data using NoSQL APIs of your choice – DocumentDB SQL (document), MongoDB (document), Azure Table Storage (key-value), and Gremlin (graph), are all natively supported. Cosmos DB is a fully managed, enterprise ready and trustworthy service. All your data is fully and transparently encrypted and secure by default. Cosmos DB is ISO, FedRAMP, EU, HIPAA, and PCI compliant as well.

委細について、本日深夜(日本時間)に開催される Build 2017 での発表が気になるところです。

なお、ライブビューイングがストリーミング配信される様ですので、そちらも見ておくと良いと思われます。

Azure Functionsのパフォーマンスに関する実験

昨日、ほんの出来心でMojoアプリをAzure Functionsで動かせたらどうだろうなどと考えてしまい、2時間弱くらいでMojo::Server::AzureFunctionsというものを作りました。

Mojo::Server::AzureFunctionsを作って使うまで

もともとAzure FunctionsのHTTP TriggerとHTTP output bindingをハンドリングするためのアダプタとして作ろうとあらかじめ決めており、Functions自体はリクエストごとにプロセスを起動する動作モデルであるため、これはCGIと同様であるとみなすことができました。

従って、既存のMojo::Server::CGIを参考に、環境変数の解析と入出力の調整だけでどうにかMojoアプリをAzure Functionsで動作させることに成功しました。

動作確認したソースコードはこちらのレポジトリに上げてあります。

苦しかった点

Mojoでは $c->render(json => {...}); とすることでJSONによるレスポンスを行うことができます。

ところが、Mojo::ServerMojo::Messageではrenderメソッドの第1引数に渡された値を一切検出できません。

そのため、レスポンスのContent-TypeやContentそのものを検証して、ContentがJSON Stringであるかどうかを検証する必要があり、そうしなければAzure FunctionsのHTTP output bindingでは単なる文字列として判別されてしまい、<string>{"message": "hello"}</string> という感じの残念なレスポンスを返してしまうことになるのです。

これを回避するコードがココでして、Contentが{で始まっていれば多分JSONだろうとみなし、evalで囲った中でdecode_jsonをかけるという非常に野蛮な方法で対応したことがお分かり頂けると思います。

実際に動作させてみた

さて、Azure Functionsにデプロイして実際に動作させてみた結果をお見せします。

Azure Functionsの大変便利な機能の一つとして、関数ごとにレスポンスにかかった時間のモニターをすることができる、その名もずばり「モニター」という機能があります。今回はそこで記録された動作速度を見ていただきたいと思います。

一番右側の丸括弧で囲われた値がレスポンスまでにかかった時間です。平均およそ23秒ほどでしょうか。これは遅いですね!

なぜ遅いか?

実は遅い理由は非常に単純でして、Mojoliciousをuseするコストがあまりにも高いことが挙げられます。これはMojoliciousが悪いわけでもありませんし、もちろんAzure Functionsが悪いわけでもありません。

どういうことかというと、そもそもAzure Functionsには非常にシンプルなものがデプロイされることが期待されています。Webアプリケーションフレームワークのような複雑なコードは毎回ロードするだけでもコストがかかりますので、当然のようにレスポンスまで時間がかかるのです。

証拠

例えば関数内のrun.shの内容が以下のように非常に単純なものだったとします。

perl -MJSON::PP -le 'print encode_json({message => "Hello, World!"})' > $res

その場合は、以下のようなレスポンス速度を実現することができるのです。

概ね0.5〜2秒程度でレスポンスを返すことができています。必要十分な速度と言えますね。

まとめ

Azure FunctionsでMojoアプリを動作可能にするMojo::Server::AzureFunctionsを作って、レスポンス速度を確かめてみました。

その結果わかったのは、Azure Functionsには複雑なコードをデプロイすべきではなく、徹底的にシンプルなコードをデプロイすることでその真価を発揮することができる、ということです。その先にはほぼ無限のスケーラビリティとメンテナンスフリーにほど近いシステムの実現があると私は思います。

Sisimai ♥ Azure Functions

先日のHokkaido.pmの帰りに時間が余ってしまったので、新千歳空港のロビーでダラダラしながら、SisimaiをAzure Functionsで動作させてみました。

Azure FunctionsでPerlが動く

Azure Functions上では、制約がありながらもPerlが稼働します。これは既にHokkaido.pmで発表した通りです。

Sisimaiとは

Sisimai

Sisimaiとは、バウンスメールの解析を行い、その情報を取得したりJSONに変換するためのライブラリです。
Perl版とRuby版が二条項BSDライセンスにて提供されており、GitHub上で開発されています。

本エントリではこれ以降、単にSisimaiと表記する場合は、特筆がない限りPerl版のp5-Sisimaiを指すものとします。

小さく堅牢なSisimaiに注目

ちょうどHokkaido.pmにSisimai作者のazumakuniyukiさんがいましたので、Sisimaiの依存モジュールについて聞いてみました。

わいとん:Sisimaiって依存モジュールどのくらいあるんです?

azumakuniyukiさん:確かJSONとClass::Accessor::Liteくらいだったんちゃうかなぁ・・・「小さく堅牢に」っちゅうのを心がけてるんで、そのくらいやったと思います。

実際metacpanのdependencyを見ても非常に依存が少なく、またXSを利用したモジュールに依存していません。Azure Functionsで稼働させる対象としては適任であると考えられます。

実際に作った

実は去年の夏にSisimaiに関連したエントリを公開しております。

当時はSisimaiが稼働するDockerコンテナを作りましたが、今年はSisimaiが稼働するサーバレスシステムを作りました。

Azure Portalにアクセスできる状態なら、Deploy to Azureボタンを押すだけでデプロイできるようにしてあります。

デプロイが終わったら、適宜バウンスメールが含まれたMailbox形式のファイルを以下のようにPOSTしてみてください。

ENDPOINT_URL="https://yourfuncname.azurewebsites.net/api/sisimai?code=..."
curl -X POST 
     -H "Content-Type: text/plain" 
     --data-binary @path/to/mailbox 
     $ENDPOINT_URL

以下が動作イメージです。

image

作る上でキモとなった点

Sisimaiをどうやってインストールするか

まずローカル環境でcpanfileを作って以下のような内容とします。

requires 'Sisimai';

あとはcpm installするとlocal以下にSisimaiとその依存モジュールが入ってきます。

これだけです。

それらをどうやってアップロードするのか

基本的にはGitHubにあがっていれば、Functionsにそのまま落としてくることが可能ですので、ひとまずGitHubにおきました。

Azure Functions の継続的なデプロイというエントリにそのやり方が書いてありますので、開発中はこの方法でデプロイ済みのAzure Functionsにファイルをアップロードしていきました。

ARM Template

これが一番難しかったのですが、以下のドキュメントを読みながら地道に作っていきました。

まとめ

小さく堅牢に作られたSisimaiをAzure Functionsにデプロイする方法を自動化し、数クリックでデプロイできるようにしました。またそのデプロイボタンを一般公開しました。

エラーメールの解析をサービスとして展開できるので、ぜひお試しいただけると幸いです。

Hokkaido.pm 13でサーバレスアーキテクチャの話をしてきた

昨日札幌で開催されたHokkaido.pm 13に参加し、Azure Functionsを使ったサーバレスアーキテクチャについて発表してきました。

大まかに以下の項目について話しました。

  • サーバレスアーキテクチャの概要
  • 歴史と現状
  • Azure Functionsを使う理由
  • Azure FunctionsとPerl
  • 実例とデモ

持参した回線の調子がよろしくなく、20分ほどの時間オーバーという大失態をしでかしてしまいましたが、温かく見守ってくださった皆様には、この場を借りて改めて御礼申し上げます。ありがとうございました。

落穂拾い

発表では説明しきれなかったことをざっくりとここでフォローアップしていきたいと思います。

Function Appへの関数のデプロイ

基本的にはAzure App Serviceのデプロイ方法に沿ってデプロイします。
Azure App Service へのアプリのデプロイという公式のエントリがありますので、詳しくはそちらを参照していただきたいと思います。
なお、ざっくりではありますが、以下のデプロイ方法が利用可能です。

  • FTP
  • FTPS
  • git(ローカルgitという方法で、App Service側にremote reposが作られる方法)
  • github
  • OneDrive
  • Dropbox
  • BitBucket
  • 外部gitレポジトリ(公開レポジトリにpushするとデプロイされる)

テスト手法について

基本的にFunctionsの環境を模倣するものはFunctions以外にありませんので、Azure上にテスト用のFunctionsを起こしておくのが良いと思います。
Functionsのダッシュボードにはテスト用のリクエスト発行をする仕組みが備わっていますので、そちらを使うのがスムーズでしょう。

CIについて

継続テストをもしやるのであれば、上述したテスト手法をガンガン定期的に実施する以外の方法はないと思います。
当然データストアなどへの副作用も発生しますので、その辺を考えて継続テストを行わないといけないです。
そんなわけで、今の時点ではあまり現実的な解は見つけられていませんが、どなたか知見のある方のご意見をいただけると幸甚です。

最後に

私個人としてはPerl5に対して、クラウド向けのグルー言語として、今後もまだまだ利用価値があると考えています。特にPure Perlで記述されたライブラリやロジックは、サーバレスな環境において、大変に便利な存在となり得ると思います。

今回そのことを、北海道という場所で語れたことを非常に嬉しく思っております。主催の永谷氏と参加メンバーの皆さん、そして会場についてのサポートをしてくださったJPAの皆様、本当にありがとうございました。