バッチ処理のためのメッセージキューとしてMicrosoftのAzure Storage Queueを使っている。C#での使い勝手は言わずもがなだし、Rubyでも最低限のSDKが提供されているのでAWSと比べて使い勝手が悪いということはない。

ただ、Rubyでメッセージを追加してC#でメッセージを取り出すということをやっていたらエンコード関係ではまったのでその部分について今日はメモしておきたい。

例外が発生するケース

Rubyでメッセージ追加

基本的には公式のドキュメントを参考にした。

キュー サービスを使用する方法 (Ruby) | Microsoft Azure

メッセージを追加はRailsアプリケーションから行いたかったので、上のドキュメントを参考にしてこんな風にかいた。

1# config/initializer/azure.rb
2Azure.configure do |config|
3    config.storage_account_name = ENV['AZURE_STORAGE_ACCOUNT']
4    config.storage_access_key   = ENV['AZURE_STORAGE_ACCESS_KEY']
5end
6
7AZURE_QUEUE_SERVICE = Azure::QueueService.new
8AZURE_QUEUE = ENV['AZURE_QUEUE']
9AZURE_QUEUE_SERVICE.create_queue(AZURE_QUEUE)
1# in controller
2AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s)

C#でメッセージ取り出し

同じく公式ドキュメントを参考にして、メッセージを取り出す部分を書いた。

 1private void Func()
 2{
 3    var sc = new StorageCredentials( Constants.AZURE_STOREGE_ACCOUNT, Constants.AZURE_STOREGE_ACCSESS_KEY );
 4    var storageAccount = new CloudStorageAccount( sc, true );
 5    var queueClient = storageAccount.CreateCloudQueueClient();
 6    var queue = queueClient.GetQueueReference( Constants.AZURE_QUEUE );
 7    queue.CreateIfNotExists();
 8    var timeSpan = new System.TimeSpan(1, 0, 0);
 9    var message = queue.GetMessage( timeSpan );
10    messsage.AsString;
11}

例外発生

実装はめちゃくちゃ簡単だしこれで動くだろうと思ったのだが、message.AsStringの部分でBase-64 文字配列または文字列の長さが無効です。という例外が発生してしまった。しかもAzure Storage Explorerなどのツールでも同じ例外が起こった。

SDKのソースを見てみたところ、メッセージにはエンコードした文字列かそうでないものかのタイプを持っているらしいが、どうやら今回のケースでは部エンコードされていない文字列であるにもかかわらず、タイプがエンコード済となっていて、想定外のコードを通ることになってしまっていたらしい。

azure-storage-net/CloudQueueMessage.Common.cs at master · Azure/azure-storage-net

 1/// <summary>
 2/// Gets the content of the message, as a string.
 3/// </summary>
 4/// <value>A string containing the message content.</value>
 5public string AsString
 6{
 7    get
 8      {
 9          if (this.MessageType == QueueMessageType.RawString)
10              {
11                  return this.RawString;
12              }
13          else
14              {
15                  byte[] messageData = Convert.FromBase64String(this.RawString);
16                  return utf8Encoder.GetString(messageData, 0, messageData.Length);
17              }
18      }
19}

回避策

これもソースをみたらわかったんだけど、RubyのSDKでエンキューするときにエンコードオプションを指定できるみたいだった。

 1def create_message(queue_name, message_text, options={})
 2  query = { }
 3  unless options.empty?
 4    query["visibilitytimeout"] = options[:visibility_timeout] if options[:visibility_timeout]
 5    query["messagettl"] = options[:message_ttl] if options[:message_ttl]
 6    query["timeout"] = options[:timeout].to_s if options[:timeout]
 7  end
 8  uri = messages_uri(queue_name, query)
 9  body = Serialization.message_to_xml(message_text, options[:encode])
10  call(:post, uri, body, {})
11  nil
12end

options[:encode]の部分!

そういうわけで、最初の実装にオプションを追加してこうしてあげればよかった。

1# in controller
2AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s, encode: true)

例外もでなくなりました。めでたし!