Azure Storage QueueでBase64関係の例外
移転しました →
バッチ処理のためのメッセージキューとしてMicrosoftのAzure Storage Queueを使っている。C#での使い勝手は言わずもがなだし、Rubyでも最低限のSDKが提供されているのでAWSと比べて使い勝手が悪いということはない。
ただ、Rubyでメッセージを追加してC#でメッセージを取り出すということをやっていたらエンコード関係ではまったのでその部分について今日はメモしておきたい。
例外が発生するケース
Rubyでメッセージ追加
基本的には公式のドキュメントを参考にした。
キュー サービスを使用する方法 (Ruby) | Microsoft Azure
メッセージを追加はRailsアプリケーションから行いたかったので、上のドキュメントを参考にしてこんな風にかいた。
# config/initializer/azure.rb Azure.configure do |config| config.storage_account_name = ENV['AZURE_STORAGE_ACCOUNT'] config.storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY'] end AZURE_QUEUE_SERVICE = Azure::QueueService.new AZURE_QUEUE = ENV['AZURE_QUEUE'] AZURE_QUEUE_SERVICE.create_queue(AZURE_QUEUE)
# in controller AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s)
C#でメッセージ取り出し
同じく公式ドキュメントを参考にして、メッセージを取り出す部分を書いた。
private void Func() { var sc = new StorageCredentials( Constants.AZURE_STOREGE_ACCOUNT, Constants.AZURE_STOREGE_ACCSESS_KEY ); var storageAccount = new CloudStorageAccount( sc, true ); var queueClient = storageAccount.CreateCloudQueueClient(); var queue = queueClient.GetQueueReference( Constants.AZURE_QUEUE ); queue.CreateIfNotExists(); var timeSpan = new System.TimeSpan(1, 0, 0); var message = queue.GetMessage( timeSpan ); messsage.AsString; }
例外発生
実装はめちゃくちゃ簡単だしこれで動くだろうと思ったのだが、message.AsString
の部分でBase-64 文字配列または文字列の長さが無効です。
という例外が発生してしまった。しかもAzure Storage Explorerなどのツールでも同じ例外が起こった。
SDKのソースを見てみたところ、メッセージにはエンコードした文字列かそうでないものかのタイプを持っているらしいが、どうやら今回のケースでは部エンコードされていない文字列であるにもかかわらず、タイプがエンコード済となっていて、想定外のコードを通ることになってしまっていたらしい。
azure-storage-net/CloudQueueMessage.Common.cs at master · Azure/azure-storage-net
/// <summary> /// Gets the content of the message, as a string. /// </summary> /// <value>A string containing the message content.</value> public string AsString { get { if (this.MessageType == QueueMessageType.RawString) { return this.RawString; } else { byte[] messageData = Convert.FromBase64String(this.RawString); return utf8Encoder.GetString(messageData, 0, messageData.Length); } } }
回避策
これもソースをみたらわかったんだけど、RubyのSDKでエンキューするときにエンコードオプションを指定できるみたいだった。
def create_message(queue_name, message_text, options={}) query = { } unless options.empty? query["visibilitytimeout"] = options[:visibility_timeout] if options[:visibility_timeout] query["messagettl"] = options[:message_ttl] if options[:message_ttl] query["timeout"] = options[:timeout].to_s if options[:timeout] end uri = messages_uri(queue_name, query) body = Serialization.message_to_xml(message_text, options[:encode]) call(:post, uri, body, {}) nil end
options[:encode]
の部分!
そういうわけで、最初の実装にオプションを追加してこうしてあげればよかった。
# in controller AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s, encode: true)
例外もでなくなりました。めでたし!