yasutomogのブログ

Software Engineerの雑記

PHP(Lumen)からAzure Storage Tableを使ったときのメモ2

内容

yasutomo.hatenablog.com

  • 上記ブログでAzure Storage TableにPHPからアクセスしたときにハマったポイントをまとめたメモ

処理概要

  • CSVから読み込んだデータをAzure Storage Tableに保持
  • CSVの1行がAzure Storage Tableの1Entityという構造
  • CSVのKey項目が存在しなければInsert(登録)、Key項目が存在すればUpdate(更新)する
  • CSVのKey項目でStorageから検索して、登録または更新を判定
  • Azure Storage TableのEntityのプロパティはすべて、String(文字列)の型を指定して、CSVの記載のまま登録したい

ハマったポイント1

Azure Storage Tableから取得した値が想定外!!?

  • 設定したときは、String(文字列)を指定
$entity->addProperty("hoge", EdmType::STRING, csvRec[0]);
  • プロパティから取得するときは、getValue関数を使用していた
$entity->getProperty("hoge")->getValue();
  • 設定した値のまま取得できることを想定していた。 基本的には想定通りの動作だったが、小数点付きの数値などの場合に設定値と異なることがあった
  • 設定値が "0001.0" の場合。getValueすると、 "1" で返されることを確認
  • azure-storage-tableのライブラリの中を確認
    • MicrosoftAzure\Storage\Table\Internal の JsonODataReaderWriter クラスでStorageから取得したEntityを変換している
    • 上記クラスのparseOneEntity(private関数)でEntityを生成してプロパティを1つずつ設定している
    • プロパティの型がStringの場合に、strvalした値をvalueに設定している
    • strval(0001.0)の戻り値は、1となるため、今回の現象になっている
  • Entityから以下のようにすると、想定通り、設定値と同様(0001.0)の値が取得できる
$entity->getProperty("hoge")->getRawValue();

ハマったポイント2

Entityを更新すると想定してプロパティの型が想定外!!?

  • entityを更新するときには、Storageから取得したentityを元に一部のプロパティを変更していた
$tableClient = TableRestProxy::createTableService($connectionString);
$entity = $tableClient->getEntity("mytable", "tasksSeattle", 1);
$entity->setPropertyValue("hoge", "fuga");
$tableClient->updateEntity("mytable", $entity);
  • 上記コード例のような場合、変更したプロパティは想定通りだったが、変更していないプロパティが元の型と別の型に自動的に変わっていることを確認
  • 上記コード例でentityに「weight」というプロパティに「60.5」という値が設定されていた場合、updateEntityされると元々はString型だったものがDouble型に自動で変換されている。また日付型データも同様の現象が発生することを確認
  • 取り急ぎの解決策として、Storageから取得したentityはそのまま使わないようにして、別途Entityを生成してaddPropertyすることで、すべてのプロパティをString型で保持するようにした

ハマったポイント3

getRawValueの値が想定外!!?

  • ハマったポイント1で書いたとおり、getRawValue関数を使うことで設定したときの値がそのまま返されることを確認していた
  • プロパティの型がString以外のときは、空文字が返されることを確認
  • これについてはライブラリの仕様だと思う。ライブラリのソースコードの中にも「//Store the raw value of the string representation.」というコメントが記載されており、あくまでも文字列型のときにのみ使用されるものという認識。

PHP(Lumen)からAzure Storage Tableを使ったときのメモ1

内容

  • Lumenで構築しているWebアプリケーションからAzure Storage Tableを使ったときのメモ
  • PHPからの接続は、MSの公開ライブラリを使用(以下の方法でライブラリ追加)
    • composer require microsoft/azure-storage-table

バージョン

github.com

前準備

ストレージアカウントを作成

  • 以下のリンクを参考にして、ストレージアカウントを作成し適当な名前でTableを新規作成

docs.microsoft.com

Azure Storage Tableを確認するためのツールをインストール

  • Azure Portalからは現時点ではTableが確認できてない
  • MSから公式のクライアントツールが提供されている(Mac版もある)

docs.microsoft.com

ライブラリの使用方法

  • 基本的には以下のリンクの方法でTable操作とEntity操作は問題なくいける

docs.microsoft.com

実装ポイント

Entityの検索処理

  • Key検索ではなくパーティションを条件に全件検索などするときに、1度に返される結果件数は1000件の制限がある
  • 検索結果が1000件より多い場合は、検索結果のcontinuationTokenを使用して次の1000件を取得するような実装が必要になる

サンプルコード

// SampleTableからの検索結果
$entities = [];
do {
    $result = $tableClient->queryEntities('SampleTable', $options);
    $entities = array_merge($entities, $result->getEntities());
    $options->setContinuationToken($result->getContinuationToken());
} while($result->getContinuationToken());

複数Entity操作処理

  • 複数行まとめて追加や更新などしたいときには、Batch処理としてまとめて処理することが可能
  • 1Entityごとに処理するよりもBatch処理を使用する方がパフォーマンス的にも良い
  • 但し、1Entityに対しての複数操作をBatchでまとめるのはやめてというのがドキュメントに記載されている

サンプルコード

$batchOperations = new BatchOperations();
foreach ($records as $rec) {
    $entity = new Entity();
    $entity->setPartitionKey('hoge');
    $entity->setRowKey('fuga');
    $entity->addProperty("propKey1", 'propVal1');
    $entity->addProperty("propKey2", 'propVal2');

    if (count($batchOperations->getOperations()) % 100 === 0) {
        $tableClient->batch($batchOperations);
        $batchOperations = new BatchOperations();

    }
}

if (count($batchOperations->getOperations()) % 100 !== 0) {
    $tableClient->batch($batchOperations);
}

Board APIのデータをChart表示するElectronアプリ(vue.js)

概要

  • ElectronとBoard APIを使ったサンプルアプリ作成(素振り)
  • Board APIで請求データと支払データを取得してChart表示
  • 基盤はelectron-vueを使用して実装
  • CSSフレームワークにはbuefyを使用
  • ストレージにnedbを使用

使用技術

  • electorn:2.0.2
  • vue:2.3.3
  • vue-electron:1.0.6
  • bignumber.js:7.0.1
  • buefy:0.6.5
  • chart.js:2.7.2
  • nedb:1.8.0

github.com

github.com

buefy.github.io

今回のソース

github.com

工夫したところ

  • API接続情報をストレージに保持
    • Board APIの接続に必要となるキーとトークンは都度設定する手間を省くために、nedbで保持できるように対応
  • APIから取得したデータをストレージに保持
    • Chartを表示するときに都度、APIアクセスするのは非効率なため、nedbで保持できるように対応
  • Board APIへのアクセス方法
    • Board APIはアクセス制限(3リクエスト / 秒)があるので、一定間隔をあけつつリクエストするように対応

使用方法

1. Board APIのキーとトークンの発行

  • Board APIについては、以下を参照

developers.the-board.jp

2. 設定画面でBoard API接続情報の設定

f:id:yasug:20180609002619p:plain

  • 1で発行した接続情報を入力して設定ボタンをクリック
  • 取得ボタンは、nedbに保持しているデータを取得して表示します

3. API接続ボタンをクリックしてデータ取得

f:id:yasug:20180609002622p:plain

  • APIに接続して請求データと支払データを直近1年分取得します
  • 内部的には、月ごとのデータを保持している
  • リクエストは意図的に間隔をあけているので、少し時間がかかります

4. 請求データ画面でChart表示

f:id:yasug:20180609002626p:plain

  • APIで取得したデータを元にChartを表示します

今後

基盤はできたので、あとは必要に応じてChartの種類を追加していきたい

Board APIの利用について

会社でWebサービスのBoardを利用していて、 API(ベータ版?)が公開されているので使ってみたメモ。

API仕様については、以下にまとまっていて KEYとトークンが発行できれば、あとは仕様に沿って リクエストするという流れ。

developers.the-board.jp

最初、curlコマンドで叩いていて、問題なくレスポンスが取れることが確認できた。

レスポンス結果を使って、適当にChart(jsで描画)を表示してみることを試してみようと思い ローカル環境のWebサーバに適当なhtmlとjsでページ作成し試したところ クロスドメインのエラーとなった。

Boardのサイトからチャットにて問い合わせしたところ、すぐに担当者に繋いで頂き 現時点では、セキュリティの関係上、ブラウザからの利用は想定していなく サーバからアクセスする流れとなる。 ※チャットで即座にご回答頂けたので、非常に助かりました。

とりあえず遊びで使ってみたかっただけなので今回はローカル環境に 以下のnode.jsのプロキシサーバを立てて対応して動かしました。

github.com

BoardのAPIはリクエスト数の制限が割りと厳しいので 真面目に作るなら、サーバ側でデータ保持するなど 何かしらのキャッシュ機構を作成する必要がありそうという印象。

CordovaのAndroidビルドエラーと環境構築

前提の環境

ビルドエラーについて

cordovaで開発するときに、Android用のビルドで以下のようなエラーが発生。

Starting 'cordova-with-build'...
(省略)
Error: Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.
'cordova-with-build' errored after 5.55 s

これだけ見ると「ANDROID_HOME」を設定すればいいように見えるが環境変数として、「ANDROID_HOME」に「~/Library/Android/sdk」を設定しても同様のエラーとなり解決せず。

確認

cordova requirementsコマンドでみると、以下のようにAndroid targetがnot installedになっている。

本来であれば、ここはAndroid StudioのSDKManagerでインストールした環境が表示されるはず。

$ cordova requirements android

Requirements check results for android:
Java JDK: installed .
Android SDK: installed
Android target: not installed
Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.
Gradle: not installed
Could not find gradle wrapper within Android SDK. Might need to update your Android SDK.

対応方法

・cordova platform rm android
・cordova platform add android

上記を試してた後に、改めてcordova requirementsコマンドを実行し、targetに正しく値が表示されていることを確認してOK

$ cordova requirements android

Requirements check results for android:
Java JDK: installed .
Android SDK: installed
Android target: installed android-21,android-22,android-23,android-24,android-25,android-26,android-27
Gradle: installed

iOSのPush通知(Apple Push Notification)のpemファイル作成

Push通知周りの証明書作成でいつもはまるのでメモ

現象

  • p12ファイル作成後、以下のようなopensslコマンドでpemファイルを生成
  • p12ファイルを生成するときに設定したパスワードを聞かれるので入力
  • Can't read Password というエラーが発生し、0バイトのpemファイルが作成される
openssl pkcs12 -in hoge.p12 -out fuga.pem -nodes -clcerts

解決策

  • iterm2を使って上記コマンドを叩くとエラーになるので、Macにデフォルトで入っているターミナルで全く同じコマンドを叩くことで正常にpemファイルが生成される

App Service(Microsoft Azure)とAzure SQL DatabaseのReconfiguration対応

概要

  • Azure環境でシステム開発している中で、Reconfigurationという現象に遭遇したので、その時の対応についてまとめる

Reconfigurationについて

  • Azure SQL Databaseには、サービスの仕様としてReconfigurationというものがある
  • Reconfigurationが発生すると、数秒〜数十秒の間、DB接続ができなくなる
  • Reconfigurationは、不定期で発生する(現状だと1ヶ月に2、3度)
  • Reconfigurationそのものは、安定したDBの稼働のために必要なものらしい
  • Azure SQL Databaseを使用するには、Reconfigurationが発生することを前提で開発する必要がある

[SQL Database] Reconfiguration (リコンフィグレーション) は悪ではない。 – Microsoft SQL Server Japan Support Team Blog

環境

本番環境

開発環境

Microsoftの公式ドキュメントの対処方法

  • Microsoftの公式ドキュメントでは、Reconfgiuration発生を検知し、wait後にリトライを実装する流れを推奨。
  • Reconfiguration発生時に使用した接続は使用できないため、改めて接続し直す必要がある。

[SQL Database] アプリケーション作成における推奨事項について (Microsoft Azure SQL Database) – Microsoft SQL Server Japan Support Team Blog

テスト方法

  • 基本的には、Reconfigurationは不定期に発生するが、意図的に発生させることも可能。
  • Azure SQL Databaseのプランを変更することで、プランが切り替わる間の数秒間だけReconfigurationが発生。
  • Azure Portal上からプラン変更して、SQL実行を連続して実行することでテスト可能。

ハマるポイント

Reconfigurationの判断

  • 本番でDB接続エラーが発生したときに、それがReconfigurationによるものなのかは現在MSサポートに問い合わせる必要がある。 以前までは、sys_event.logに出力されていたらしいが、現在は出力されない。

V12 の Azure SQL Database の sys.event_log に reconfiguration が記録されない (Document Version 1.0) – Microsoft SQL Server Japan Support Team Blog

  • Microsoft公式ドキュメントでは、Reconfiguration発生の判断をSQLのERRCODEで制御する方法を記載しているが、コネクションプーリングが有効になっていると古いコネクションを使用することでERRCODEに値が入らないことがある、そのときには、SQLSTATEしか入っていないため、そちらで判断する。(これはPHP以外の言語では大丈夫かも。ドライバの問題な気がする。)

リトライ処理をしても同様の接続エラーが発生

  • Lumenでは、リクエスト単位でPDOをキャッシュする機構がある
  • Lumenの機構を改修して、Recofiguration発生時にのみ、PDOを再生成して失敗したSQL実行という流れを単純に作って対応したが、 Reconfiguration発生時と同様の接続エラーが発生
    • Azure App Service(Windowsサーバ)上では、コネクションプーリングがデフォルトで有効になっている
    • Linux環境では、コネクションプーリングの設定はデフォルト無効となっている(開発環境はCentOSだったため、本番のみ再現することでハマる、、)
  • PDOを再生成しても内部ではReconfiguration発生時のプールを使用しているため、エラーが発生する

App Serviceのコネクションプーリングの制御

  • App Serviceのプラン(サーバスペック)によって、プールの最大数などが決まる仕様になっている
  • PHPからプールを破棄するAPIなどは用意されていない
  • PHPからアクティブなプール数を取得するAPIなどは用意されていない
  • コネクションプーリングを無効化すると、その都度確実に接続しにいくため、エラーが発生し続けることはない
    • 但し、遅くなる。

最終的な対応方法

  • 基本方針として、コネクションプーリングを一律無効化するのは、パフォーマンスの観点からNGなため、デフォルトはプールを有効化
  • 現状は対応1でも問題なく動いているため、様子見。これでも駄目な場合、別途キューのプロセス管理が必要になるが、対応2を検討

対応1

  • Reconfigurationを検知したときに、wait後、コネクションプーリングを無効化したPDOを生成し失敗時のSQLを再実行

対応2

  • Reconfigurationを検知したときに、システム全体でコネクションプールを無効化するように制御(PDO生成周りの処理改修)
  • キューに90秒後コネクションプールを有効化する処理を追加(プールの生存期間が60秒なため、若干の余裕を持たせる)