db tech showcase 2017(1日目)に参加した話

今回の投稿は、いつもの、技術を論じるブログではなく読み物です^^;

2017/9/5、db tech showcase 2017に参加しました。

私はDatabaseを軸として打ち出しているイベント(セミナー)に参加したのは実は初めてだったりします(「私はDB屋では無い」のです)。
秋葉原UDXでがっつり3日間開催されました(されています)が、これ、参加費用無料!なんですよね^^
ミニ展示スペースがあった(&プロダクト宣伝的セッションもあった)ので、スポンサー費用(+インサイトテクノロジー様の持ち出し?)なんかで運用されてるのかな?・・・なんて金勘定の頭が働いてしまうは私が穢れたエンジニアだからでしょうか^^;

初日の本日は2コマ目から参加させていただきました。

では、以下に感想をポロポロと・・・

1. 「A12:Keynote Speech これからの'‘本命技術’‘はこう見つける!~ポスト・リレーショナルデータベース時代を読み解くコツ~」

まずは、キーノート2として、ウルシステムズ株式会社 漆原 様 / 楽天株式会社 森 様によるトークセッションでした。
特定の技術に深く踏み込むものではなく、DBを中心として業界全体を俯瞰した形でのトーク、また、楽天技術研究所というテクノロジーの集まる現場における、技術との付き合い方(まとめすぎかな?)的な内容のトークが展開されました。

個人的に最も感じたのは、「お二方の下で働くエンジニアは幸せだろうな」ということです。
ビジネスを推進する要素は「ビジネス的視点を起点とするもの」「技術的視点を起点とするもの」の2つがあると私は信じています。
前者が絶対であり、技術を語るものは「ビジネスを理解しない ただのオタ」と判断される組織も日本には多々あるように感じています。でも、それが真であるケースも多々あるか・・・。

でも、やはり、最終的にテクノロジーによりサービスは顧客に対して提供されており、同時にビジネス的観点も重要であると思っています。
ビジネスマンとエンジニア、互いが、互いを否定しあうのではなく、理解しあって、「顧客に対する価値を提供できた」時が最も大きな力を発揮するような気がしているのです。

2. 「A13:MySQL MySQLを割と一人で300台管理する技術」

すみません、私はDB界隈の人間ではないので「@yoku0825さん」を存じ上げていなかったのですが、セッションは何気に楽しめました。
完全にDBエンジニアセッションでした。私はMySQLの面倒を見る仕事をしたことがなかったので、心に沁みる「おーー」という気持ちにはならなかったのですが、実運用する上で、ここ気を付けようよ、というノウハウが詰まっていて、「是非このパワポスライド欲しいわー」って感じのセッションでした。
あとGMOグループってよく耳にする企業なので、「へー、こんな感じなんだなぁ。MySQLを軸にしてるんだぁ」といった感覚を覚えました(全グループ企業の状況なんかは、もっと詳細を聞かないと分からないと思いますが・・・)

3. 「A17 データベース・ビフォーアフター ~インメモリーによる超高速化の世界~」

再び ウルシステムズ株式会社 様のセッションです。
漆原社長は、プレゼン上手&惹き付け上手 ですね^^
いえ、まあそれを除外しても、このセッションは面白かった気がしました。
apache geode」を使ったインメモリグリッドデータベースによるパフォーマンス改善事例の紹介、でした。

冒頭の「私はDB屋では無い」宣言の通り、従来のRDB文化をぶち壊すアーキテクチャが私の好物です^^
なので「インメモリグリッドデータベース」で、旧来のRDBシステムのパフォーマンスボトルネックを非RDBでぶっちぎる事例はすごくワクワクしました。
セッションのお話を聞いた限り、apache geodeは結構自動的に水平スケーリング&分散(って言っていいのかな)してくれるそうなので、これはちょっと自分でもリサーチしてみたい気になりました。

4. まとめ

上記以外のセッションも参加させていただいたのですが、特に印象的なセッションのみ感想を書かせていただきました。
あー、でも「E15 分散グラフデータベース - DataStax Enterprise Graph 森下 雄貴 様」も自分好みでしたよ。
でも、グラフデータベースはAzure Cosmos DBを学ぶと同時に学習していたので、心に刺さるレベルとまではいかなかったかも・・
まあ、私は「RDB駆逐してやるぜ!」という意気込みの「アプリケーションエンジニア」として参加させていただきましたが、十分に楽しめた気がしています。
(過激表現、ご勘弁を・・・)
まあ、なんだかんだ言いながら・・・
アプリケーションエンジニアもデータベースエンジニアも、進化や改善を求め歩みを止め無いことが最も素晴らしいことではないかと思っています!!

Dictionary to Objectのマッパーを使う - Slapper.AutoMapper

本日、会社でDictionaryオブジェクトからObjectへのオートマッパーを行う方法知りませんかー?と聞かれまして・・・
決め打ちの固定プロパティへのマッピングであれば、AutoMapperのMapFromで出来るかなぁ・・・と思ったのですが・・・
要件としてはプロパティ名を決め打ちしない動的マッピングなのです。

ということでググったら、StackOverflow経由で Slapper.Automapper なるものを見つけましたので、ここにメモしておきます。
といっても結構前からgithubに上げられていたライブラリになります。

Slapper.AutoMapper

これです!

github.com

普通にVSからNugetで取得することができます。

Slapper!!!なんかかっこいいですね。そのままの動きをします。

何をするもの?

まあ、つまり「Dictionaryオブジェクトから任意のオブジェクトへのマッパー」を可能にします。

以下の リスト1 のオブジェクトを、一発で リスト2 のオブジェクトにマッピングします。

// リスト1
var src = new Dictionary<string, object>();
src.Add("IntProp1", 10);
src.Add("IntProp2", 20);
src.Add("IntProp3", 30);
src.Add("IntProp4", 40);
src.Add("IntProp5", 50);
src.Add("StringProp1", "10");
src.Add("StringProp2", "20");
src.Add("StringProp3", "30");
src.Add("StringProp4", "40");
src.Add("StringProp5", "50");
// リスト2
public class TestClass
{
  public int IntProp1 { get; set; }
  public int IntProp2 { get; set; }
  public int IntProp3 { get; set; }
  public int IntProp4 { get; set; }
  public int IntProp5 { get; set; }

  public string StringProp1 { get; set; }
  public string StringProp2 { get; set; }
  public string StringProp3 { get; set; }
  public string StringProp4 { get; set; }
  public string StringProp5 { get; set; }
}

使い方

以下のリスト3が使い方のサンプルです。

// リスト3
namespace ConsoleApp1 {
  class Program {
    static void Main(string[] args) {
      // データソースを作成
      Dictionary<string, object> src = new Dictionary<string, object>();
      src.Add("IntProp1", 10);
      src.Add("IntProp2", 20);
      src.Add("IntProp3", 30);
      src.Add("IntProp4", 40);
      src.Add("IntProp5", 50);
      src.Add("IntProp6", 60);  // マップ先に存在しないプロパティは無視される
      src.Add("StringProp1", "10");
      src.Add("StringProp2", "20");
      src.Add("StringProp3", "30");
      src.Add("StringProp4", "40");
      src.Add("StringProp5", "50");
      
      
      // 一発でマッピング!!
      var desc = Slapper.AutoMapper.Map<TestClass>(src);
    }
  }
  
  // マップ先のオブジェクト
  public class TestClass
  {
      public int IntProp1 { get; set; }
      public int IntProp2 { get; set; }
      public int IntProp3 { get; set; }
      public int IntProp4 { get; set; }
      public int IntProp5 { get; set; }

      public string StringProp1 { get; set; }
      public string StringProp2 { get; set; }
      public string StringProp3 { get; set; }
      public string StringProp4 { get; set; }
      public string StringProp5 { get; set; }

      public string NothingText { get; set; } // ソースにないプロパティはnull(number系の型なら 0 )
  }
}

ちなみに AutoMapper と名が付きますが、いわゆる本家AutoMapperは全く内部的にも使っていません。
githubで公開されているので、ソースを見ればわかりますが、シンプルなReflectionによるMapper解決を行っています。

※最近ブログをさぼっていたのでウォーミングアップ的ブログ投稿でした。
でも、何気に要求的にはよくありそうな話なので、本投稿がGoogleで引っかかってどなたかのお役に立てれば^^です!

C#7.1のAsync Mainがお気に入り~Visual Studio 2017 Update 3(15.3)

Visual Studio 2017 Update3(15.3)が 2017/8/14 にリリースされました。

www.visualstudio.com

.NET Core 2.0サポートだったり、コンパイルベースのAzure Functionsサポートのような今までPreview版を利用しなければならなかった機能が正式版で使えるようになりました。

1. C# 7.1サポート

で、その中に「C# 7.1言語機能の追加」ってものもありました。

C# 7.1機能として以下のような機能が使えるようになりました。

  • async Main methods
  • pattern-matching with generics
  • “default” literals
  • inferred tuple names

2. async Main methodのサポート

個人的には「async Main methods」が何気にお気に入りです。

普段の開発作業においてサンプル実装を試すのに、コンソールアプリプロジェクトを利用することが結構あります。
この時、従来はMain()メソッドにasyncを付けることができなかったので、以下のようなことをする必要がありました。

↓↓↓こんなやり方や・・・

// リスト1

// C# 7.0までは・・・。
// Main()以外に非同期版メソッドを用意して呼び出し
static void Main(string[] args)
{
  MainAsync().GetAwaiter().GetResult();
}

private static async Task MainAsync()
{
  string html = await new HttpClient().GetStringAsync("http://www.yahoo.co.jp");
  Console.WriteLine( html );
}

↓↓↓こんなやり方など・・・

// リスト2

// C# 7.0までは・・・。
// 試したいメソッドに .Result をつける(実際の使い方と異なる形になる)
static void Main(string[] args)
{
  string html = await new HttpClient().GetStringAsync("http://www.yahoo.co.jp").Result;
  Console.WriteLine( html );
}

C# 7.1では以下のような実装が可能になりました。

// リスト3

// C# 7.1版
// Mainにasyncが付けられる!!
static async Task Main(string[] args)
{
  string html = await new HttpClient().GetStringAsync("http://www.yahoo.co.jp");
  Console.WriteLine( html );
}

Main()メソッドにasyncが付けられるようになったことで、直接的に実装を記述することができるようになりました。

3. 利用する C# のバージョン

利用する C# のバージョンはプロジェクトのプロパティで設定することができます。
デフォルトでは「最新のメジャーバージョン」が既定になっています。つまり VS2017 Update3(15.3) で作成したプロジェクトは、そのまま C# 7.1 が利用可能です。
プロジェクトで明示的に利用する C#バージョン を設定する方法は以下の通りです。

(1) プロジェクトプロパティの表示

ソリューションエクスプローラでプロジェクトをマウス右ボタンクリック。
表示されたメニューから「プロパティ」を選択します。

f:id:daigo-knowlbo:20170815151415p:plain

(2) C#バージョンの選択

表示されたプロパティ ペインから「ビルド」を選択し、「詳細設定」ボタンをクリックします。

f:id:daigo-knowlbo:20170815151454p:plain

表示された「ビルドの詳細設定」ウィンドウで、「言語バージョン」を選択します。

f:id:daigo-knowlbo:20170815151519p:plain

f:id:daigo-knowlbo:20170815151529p:plain

Azure FunctionsでCosmos DBを入力バインドする

以前「Azure FunctionsからCosmos DBに出力バインドする」という記事を書きましたので、それと対になる形で入力バインドについても簡単にまとめておこうと思います。

ryuichi111std.hatenablog.com

今回 実装することは以下の通りです。

  • HttpTriggerを持つAzure Functionsを作成
  • Cosmos DBドキュメントを入力バインドで受け取る
  • Function内の処理で、受け取ったCosmos DBドキュメントに変更を加える

1 開発環境

利用する環境は、以下の通りです。

2 前提条件

以下のような Cosmos DB アカウント を用意している前提とします。

f:id:daigo-knowlbo:20170814014414p:plain

  • APIモデルは「DocumentDB」
  • データベース名「CompanyDB」、コレクション名「EmployeeCollection」を作成済
  • ドキュメントが1件登録済み(id=“0000000001")
-- 登録済みドキュメント --
{
  "id": "0000000001",
  "firstname": "ryuichi",
  "lastname": "daigo",
  "_rid": "5T9QALZ5bQABAAAAAAAAAA==",
  "_self": "dbs/5T9QAA==/colls/5T9QALZ5bQA=/docs/5T9QALZ5bQABAAAAAAAAAA==/",
  "_etag": "\"0300aeae-0000-0000-0000-599081c20000\"",
  "_attachments": "attachments/",
  "_ts": 1502642619
}

3 実装

Visual Studio 2017 Preview(15.3)を起動します。

(1) プロジェクト作成

メニュー「ファイル → 新規作成 → プロジェクト」を選択します。

「新しいプロジェクト」ウィンドウが表示されます。
以下の入力を行います。

  • プロジェクトテンプレート:「Visual C# → Cloud → Azure Functions」
  • 名前:適当に・・・ここではデフォルトの FunctionApp1 にしました

f:id:daigo-knowlbo:20170814014959p:plain

設定用JSONのみの空っぽのソリューションが出来上がります。

f:id:daigo-knowlbo:20170814015222p:plain

(2) Functionsコードの追加

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「追加 → 新しい項目」を選択します。

f:id:daigo-knowlbo:20170814015448p:plain

「新しい項目の追加」ウィンドウが表示されるので、「Azure Function」アイテムを追加します。
ここでは名前をデフォルトの「Function1.cs」としました。

f:id:daigo-knowlbo:20170814015604p:plain

「New Templates - Function1」というファンクションのトリガーを選択するウィンドウが表示されます。

f:id:daigo-knowlbo:20170814015713p:plain

「HttpTriggerWithParameters」を選択します。
また、「AccessRights」を「Anonumouse」としました。これにより、トリガーとなるHTTPリクエストを行う際に認証なしとなります(テストの利便性の為ここではそうしていますが、誰でもHTTPトリガーをキックできてしまうということなので、ご利用には気を付けてください)。
「FunctionName」はデフォルトの「HttpTriggerWithParametersCSharp」とします。

自動生成された Function1.cs 配下の通りです(リスト1)。

//リスト1 自動生成されたFunction1.cs

using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace FunctionApp1
{
  public static class Function1
  {
    [FunctionName("HttpTriggerWithParametersCSharp")]

    public static HttpResponseMessage Run(
      [HttpTrigger(
        AuthorizationLevel.Anonymous, 
        "get",
        "post", 
        Route = "HttpTriggerCSharp/name/{name}")]
      HttpRequestMessage req, 
      string name, 
      TraceWriter log)
    {
      log.Info("C# HTTP trigger function processed a request.");

      // Fetching the name from the path parameter in the request URL
      return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
    }
  }
}

Run()メソッドがFunctionのエントリーポイントです。
第1引数「HttpRequestMessage req」に「[HttpTrigger]属性」が付与されています。HttpTrigger属性のパラメータで以下の設定が宣言されています。

(3) CosmosDB入力バインド実装の追加

まず、CosmosDB入力バインド用エクステンションを NuGetパッケージ より追加します。

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「NuGet パッケージの管理」を選択します。

f:id:daigo-knowlbo:20170814021042p:plain

「NuGet パッケージの管理」画面が表示されたら、「参照」タブを選択し「Microsoft.Azure.WebJobs.Extensions.DocumentDB」を検索します。

f:id:daigo-knowlbo:20170814021317p:plain

Microsoft.Azure.WebJobs.Extensions.DocumentDB」を選択して、インストールボタンをクリックします。

では、 Function1.cs を開き、以下のように編集します。

// リスト2 CosmosDB入力バインド処理を追加したFunction1.cs

using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace FunctionApp1
{
  public static class Function1
  {
    [FunctionName("HttpTriggerWithParametersCSharp")]

    public static HttpResponseMessage Run(
      [HttpTrigger(
        AuthorizationLevel.Anonymous, 
        "get",
        "post", 
        Route = "HttpTriggerCSharp/name/{name}")]
      HttpRequestMessage req,
      [DocumentDB(
        "CompanyDB", 
        "EmployeeCollection",
        ConnectionStringSetting = "cosmosdb_DOCUMENTDB", 
        Id = "{name}")]
      dynamic document,
      string name, 
      TraceWriter log)
    {
      log.Info("C# HTTP trigger function processed a request.");

      // 入力ドキュメントをログ出力
      log.Info("input document info");
      log.Info(" firstname:" + document.firstname);
      log.Info(" lastname:" + document.lastname);

      // 入力ドキュメントに変更を加える
      document.age = 25;
      document.Salary = 15000000;

      // Fetching the name from the path parameter in the request URL
      return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
    }
  }
}

Function1.csへの修正ポイントは以下の通りです。

  • 引数「document」の追加
    HttpTrigger引数に続けて「document引数」を追加しています。これが CosmosDB入力バインドパラメータになります。
    パラメータには「DocumentDB属性」を付与しています。
    入力バインドの情報として「データベース名」「コレクション名」を指定しています。
    「ConnectionStringSetting」は、Cosmos DBへの接続文字列情報です。接続文字列自体ではなく、接続文字列を設定した「構成情報キー」を指定します(設定方法は後述)。
    「Id」は、入力バインドするCosmos DBドキュメントの ID を表します。ここでは「{name}」としていますが、これはHttpTriggerのパラメータ「{name}」に該当します。「nameパラメータ値 = ドキュメントID」と、意味的に少し変ですが、自動生成コードの修正を最小限にしています。

  • documentのログ出力
    document引数には ID に該当するドキュメント オブジェクトが自動的にバインド設定されて呼び出されます。
    document.firstname / document.lastname をログ出力することで、内容を確認できるようにしています。

  • documentへのプロパティ値追加
    以下のようにdocumentに対して変更を加えています。
    入力バインドしたドキュメントへの変更は、Functionの正常終了後に Cosmos DBドキュメント に反映されます(データ更新が行われます)。

// 入力ドキュメントに変更を加える
document.age = 25;
document.Salary = 15000000;

(4) 公開 & Azureポータルでの構成

では、Azure上に公開します。

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「公開」を選択します。

f:id:daigo-knowlbo:20170814022920p:plain

「新規作成」を選択して「発行」ボタンをクリックします(既にAzure FunctionsをAzure上に作成済みの場合には、「既存のものを選択」を利用します)。

f:id:daigo-knowlbo:20170814022953p:plain

Functionの名前等は適当に・・・ここでは Function Appの名前を InputBindExample として、「リソースグループ」「App Service プラン」「ストレージアカウント」は、それぞれ新規作成しました。

f:id:daigo-knowlbo:20170814023745p:plain

「公開」が成功した後の、Azureポータル画面が以下となります。
「Azure Functions(App Service)」「App Service Plan」「ストレージアカウント」の3つのリソースが作成されました。

f:id:daigo-knowlbo:20170814023859p:plain

Azure Functions「InputBindExample」を選択します。
「アプリケーション設定」をクリックして、Cosmos DBへの接続文字列項目を追加します。

f:id:daigo-knowlbo:20170814023941p:plain

f:id:daigo-knowlbo:20170814024043p:plain

(5) 実行

では、Functionを実行してみます。
まず、Azureポータルから実行します。

Function「HttpTriggerWithParametersCSharp」を選択します。
そして右側のテストタブを表示し、パラメータ「name」に「0000000001」を設定します。これは「入力パラメータname = ドキュメントID」として実装しているためです。
実行ボタンをクリックします。

f:id:daigo-knowlbo:20170814030723p:plain

出力されたログが以下の通りです。
ID = 0000000001 のドキュメント内容 firstname / lastname が出力されていることを確認することができます。つまり、確かにCosmos DBドキュメントが入力バインドパラメータとして引き渡されました。

f:id:daigo-knowlbo:20170814030753p:plain

次にAzureポータルで、Cosmos DBの「データエクスプローラ」を表示します。
age / salary 項目が追加されていることが確認できます。

f:id:daigo-knowlbo:20170814030920p:plain

匿名認証OK、HTTP GET許可としている為、ブラウザで「Https://inputbindexample.azurewebsites.net/api/HttpTriggerCSharp/name/0000000001」をアクセスする事でもFunctionをキックすることができます。

f:id:daigo-knowlbo:20170814033200p:plain

4 まとめ

ということで、Azure FunctionsへのCosmos DB入力バインドでした。
入力バインドといっても、Functionsの処理内でCosmos DBドキュメントに対して変更を加えることもできました。

Azure Functionsへの入出力バインドはCosmos DB以外にも「Blob storage」だったり、「Queue storage」だったりと、たくさんのバリエーションがあります。これらのバインドを利用すると、従来型の定型的ロジックの記述を省略することができます。
今回のCosmos DBバインドの例では、Cosmos DBへの接続処理の記述、取得・更新処理 の記述といったものが不要となりました。
つまり、煩雑なロジックの記述が Azure上のプラットフォームインフラ に吸収されていることにより、開発者はビジネスロジックの記述に注力することが可能になります。

本投稿では、ローカル実行やデバッグ等、色々端折っておりますが、Azure FunctionsへのCosmos DB入力バインドの一例として参考になればと思います。

「Serverless Meetup Tokyo #4」に参加した話

2017/8/9 「Serverless Meetup Tokyo #4」に参加してきました。

serverless.connpass.com

4回目の開催ということですが、私は初めての参加になりました。
すんごい人気なので、#5も今の時点で満員状態のようです(補欠当選も十分あるので、興味があれば、溢れてても登録しておいて損はないと思います。私も #4 では補欠繰り上げしたので^^)。

Azure勢 と AWS

で、私はこのブログを見てもわかる通り、いわゆる「Azure勢」です。
そして、この「Serverless Meetup Tokyo」は「AWS勢」が主軸です。

基本的には「Azure勢」とか「AWS勢」とか線引きをするべきではないし、したくないのですが、現在の多様化した技術世界においては、Azure / AWS の両方の技術をくまなく把握する人間というのは、ある意味限られた優秀な方々なのだと思っています。私のような凡人は、片方、もしくは両方に触手を伸ばしたとしても、どちらかに偏る事になるのだと思っています。
そして、一方に完全に偏ったとしても「Azureのすべてを知る」「AWSのすべてを知る」これ自体が非常に困難なほどの状況ではないかと思ってもいます。

楽しかったのですよ

そんな中での「Serverless Meetup Tokyo #4」だったのですが・・・

結論としては非常に楽しめました!

あのー、Azure系イベントに参加するよりも、もしかしたら(ある意味)楽しかったかも・・・これは、きちんと説明しないと語弊があるのだけれど・・・

私自身、Azureについては日々勉強を重ねています。
ですので、Azure系イベントでは、やはり「既知の領域」というものが大きくなってしまいます(あー、そんな偉そうなこと言える程ではないのだけれど・・・ご勘弁をm(_ _)m)。

しかし、AWSについては「完全な無知」ではないにしても「知らない領域」が広く存在しています。
ですから、自分自身にとって知らないことが多く、

  • 「へー、そうなんだー」

とか

  • 「このサービスはAzureでは何に該当するの?」

とか、はたまた、

  • 「あれ!?AWSだとこんな問題があるの?もしかしてAzureだと、この機能を使ったら解決出来てるんじゃないかなぁ」

とかいろいろ興味深いお話を聞くことができます。

結局・・・

そして、各セッションに対する感想はそれぞれあったのですが、今私はお酒も入っているのでざっくり、個人的総括で締めさせていただきます!

  • 「”サーバーレス アーキテクチャ”、全然 実践投入 いけるじゃん!」という自信を持てた
  • そうそう、やっぱり NoOps こそ目指すべき道だよ!人的リソースを運用メンテにかけるより顧客へのValueの提供にかけるべきだよ!

あと、個人的思い・・・

  • 20代のフレッシュエンジニアも、40代のおっさんエンジニア(&マネージャ)も楽しめて、より多くのValueを生成出来て、売上・実利を上げられる仕事がしたいよ

そして、今日のセッションでは得られていない、ビジネス的興味、そして整理したい点は以下。

  • ServerLessやPaaSを使うことのコストメリットについての整理

ではではー。
いつものブログとちょっと違うテイストになっちゃったかも・・・

【Cosmos DB】有効期限(TTL)で自動削除されるデータを作る

1 概要

Cosmos DBでは「有効期限付きデータ(ドキュメント)」というものを作ることができます。
ログ情報とかユーザーセッション情報とかのデータを ドンドコドンドコ 投入して、一定期間経過したデータは自動で削除するようなケースで利用可能です。

「よくあるファイル転送サービス(一定期間で削除される)」だったり「スナップチャット(的な)有効期限で自動削除されるサービス」だったり、そんなところでも使えるケースがあるかと思います。

ただし、ドキュメントが単独で自動削除される機能であるため、「一連の関連データを削除」というようなケースでは、単純に適用することはできないと思います。

1つ、特徴として「自動削除では RU は発生しない(RUは消費しない)」という点があります。

自前プログラムで、裏側でバッチ削除的なことをする場合、RUを消費するので、上手く使えばメリットのある機能になると思います。

1.1 設定レベル

有効期限の設定は、以下の2つのレベルに対して設定可能です。

  • 「コレクション」に対して設定する
  • 「ドキュメント」に対して設定する

1.1.1 「コレクション」に対して設定する

「Off / On(no default) / On」の3つの設定から選択します。

(1) Off

Off は、このコレクションにおけるドキュメントの有効期限自動削除を無効にします。ドキュメントに対して明示的に有効期限を設定しても、自動削除機能は働きません。

(2) On(no default)

On(no default) は、このコレクションにおけるドキュメントの有効期限自動削除を有効にします。既定の有効期限時間は設定しません。
有効期限削除を有効にしたいドキュメントに対して、明示的に時間を設定します。

(3) On

On は、このコレクションにおけるドキュメントの有効期限自動削除を有効にします。既定の有効期限時間も設定します。
既定の有効期限は、各ドキュメントでオーバーライドすることが出来ます。

また、有効期限は「秒」単位で設定可能です。

1.1.2 「ドキュメント」に対して設定する

ドキュメントに対して有効期間を設定する場合は、保存するドキュメントオブジェクトに対してJSONプロパティ名=「ttl」というint?型プロパティを追加します。

 [JsonProperty(PropertyName = "ttl", 
  NullValueHandling = NullValueHandling.Ignore)]
 public int? TimeToLive { get; set; }

※後述リスト3が完全版オブジェクト実装

1.1.3 「コレクション設定」「ドキュメント設定」の相関関係

コレクションに対する設定とドキュメントに対する設定の相関関係を表にまとめると以下の通りです。

コレクション設定 = Off コレクション設定 = On(no default) コレクション設定 = On(既定値 n秒)
ドキュメント設定 = なし 有効期限削除は無効(削除されません) 有効期限削除は無効(削除されません) 最終更新日時からn秒後に削除されます
ドキュメント設定 = -1 有効期限削除は無効(削除されません) 有効期限削除は無効(削除されません) 有効期限削除は無効(削除されません)
ドキュメント設定 = m 有効期限削除は無効(削除されません) 最終更新日時からm秒後に削除されます 最終更新日時からm秒後に削除されます

※「ドキュメント設定 = xx」と記述しましたが、コード上では int? 型として有効期限(TTL)を指定する事ができます。

1.2 削除タイミング

削除のタイミングは以下となります。

ドキュメントが最終更新されてから、
有効期限として設定された時間(秒)が経過したタイミング   

「最終更新日時(Last modified timestamp)」からの経過時間で自動削除判定が行われます。
「最終参照日時」ではないので、単純に ”一定期間利用されていないデータを削除する” といった用途には適用できないのでご注意を。

1.2.1 最終更新日時(Last modified timestamp)とは

最終更新日時(Last modified timestamp)とは、具体的には以下のものを指します。

ドキュメントの「_ts」値  

_ts値は、ドキュメントに対してCosmos DBシステムによって設定される値です。
ドキュメントの最終更新日時を表し、ドキュメントの作成時・ドキュメントの更新時に自動的に更新されます。
値は、「UNIX時間」であらわされます。

Azureポータルのデータエクスプローラでドキュメントを参照すると、以下のように _ts 値を確認することができます。

f:id:daigo-knowlbo:20170804191841p:plain

Fiddlerでドキュメント取得を行うRESTリクエストをキャプチャしても、以下のように _ts 値を確認することができます。

f:id:daigo-knowlbo:20170804191814p:plain

2 実装(コード)

では具体的な実装(コード)で「ドキュメントの有効期限設定」を行う方法を説明します。

2.1 コレクションに対して有効期限を設定する

まず「コレクションに対して」有効期限を設定する方法になります。

// リスト1 コレクションに対してドキュメントの有効期限を設定

private DocumentClient client = new DocumentClient(
  new Uri("https://cosmosdb.documents.azure.com:443/"), // あなたのURLを設定してね
  "【キー】"); // あなたのキーを設定してね

var database = 
  await client.CreateDatabaseIfNotExistsAsync(
    new Database() { Id = "ExampleDb" });
  
var collection = 
  await client.CreateDocumentCollectionIfNotExistsAsync(
    UriFactory.CreateDatabaseUri("ExampleDb"),
      new DocumentCollection()
      {
        Id = "ExampleCollection",
        DefaultTimeToLive = 60
      });

上記リスト1はデータベース(ExampleDb)とコレクション(ExampleCollection)を「(存在しなければ)新規作成」するロジックとなります。
そして、「DefaultTimeToLive = 60」の部分が「コレクションに対する、ドキュメント有効期限設定」になります。
つまり、最終更新日時から60秒でドキュメントが自動削除される設定となります。
「未設定 もしくは null」を設定すると、「Off」となります。

        DefaultTimeToLive = null

「-1」を設定すると、「On(no default)」となります。

        DefaultTimeToLive = -1

2.1.1 DocumentCollection.DefaultTimeToLive と 「Off / On(no default) / On」設定

コレクションに対する有効期限設定(DocumentCollection.DefaultTimeToLive)と「Off / On(no default) / On」の概念的設定の関連性は以下の通りとなります。

  • DocumentCollection.DefaultTimeToLive = n
    「On(no default)」に該当します。
    ドキュメントは、最終更新日時のn秒後に自動削除されます。

  • DocumentCollection.DefaultTimeToLive = null
    「Off」に該当します。

  • DocumentCollection.DefaultTimeToLive = -1
    「On(no default)」に該当します。
    ドキュメントの自動削除は有効ですが、自動削除時間はドキュメントの設定に依存します。

2.2 ドキュメントに対して有効期限を設定する

「個々のドキュメントに対して」有効期限を設定する例が以下のリスト2になります。

// リスト2 自動削除までの時間=100秒のドキュメントを作成

// コレクションに保存するオブジェクトを作成
UserAccount userAccount = 
  new UserAccount() { 
    UserID = "ryuichi111std", 
    UserName = "Ryuichi Daigo", 
    UpdateCount = 0, 
    TimeToLive=100 };

// オブジェクトをコレクションに保存(ドキュメントとして保存)
var document = await client.CreateDocumentAsync(
  UriFactory.CreateDocumentCollectionUri("ExampleDb", "ExampleCollection"),
  userAccount);

Cosmos DB(DocumentDB)に保存するデータ型「UserAccount」が唐突に登場していますが、これは以下のリスト3の型となります。
「TimeToLiveプロパティ」値が有効期限(TTL)となります(ドキュメントの有効期限は100秒)。
リスト3にあるように「jsonプロパティ名 = ttl」とします。これがドキュメントの有効期限を示すプロパティとなります。

// リスト3 保存するドキュメントの型はこれ

using System;
using Newtonsoft.Json;

namespace AutoExpireDataExample
{
  public class UserAccount
  {
    [JsonProperty(PropertyName = "id")]

    public string UserID { get; set; }

    public string UserName { get; set; }

    public int UpdateCount { get; set; }

    [JsonProperty(PropertyName = "ttl", NullValueHandling = NullValueHandling.Ignore)]
    public int? TimeToLive { get; set; }
  }
}

3 まとめ

ということで「有効期限で自動削除されるデータ(ドキュメント)」についてのエントリーでした。
ドキュメントの削除処理に関したは「単に特定コレクションドキュメントが単独で消えていい例」それから「プログラマティックに関連データも同時にカスタム実行により削除したい例」とあると思います。
その上で、RU消費のない「ドキュメントの有効期限自動削除」は魅力的な機能でもあります。
使いどころの難しさはありますが、うまく活用すれば有益な機能の1つであると思います。

<2017年8月版>Xamarin + Prism 超入門(とりあえず動かしてみよう!)

最近の本ブログへのトラフィックのうち、以前に書いた以下の記事へのものが未だに多いようです。

ryuichi111std.hatenablog.com

ただし、上記記事では開発環境が Visual Studio 2015 であり、少し古いバージョンをベースとしています。

その為、[2017年8月版]として最新の開発環境である「Visual Studio 2017(Windows)」をベースに「Xamarin + Prism 超入門(とりあえず動かしてみよう!)」を書き起こしたいと思います(厳密には Xamarin Forms + Prism です)。

1 前提条件

本編に入る前・・・以下を前提条件とします。

  1. Visual Studio 2017がインストールされている事(Windowsの場合)
  2. Visual Studio 2017において「.NETによるモバイル開発」がインストールされている事
  3. iOS / Android / UWP(VS2017のみ) 実行環境が整っている事

2 はこれ↓です。

f:id:daigo-knowlbo:20170803012230p:plain

3 については本エントリーの主題ではありませんが、Xamarin開発を始める際に引っかかる事が多い点なので、いくつかのポイントを以下に説明しておこうと思います。

1.1 iPhoneシミュレータ(iOS)実行環境設定について

Mac側およびVisual Studio 2017側で設定を行います。

1.1.1 Mac側セキュリティ設定の変更

まず、Mac側でセキュリティに関する設定を行う必要があります。
「システム環境設定」ウィンドウで「共有」をクリックします。

f:id:daigo-knowlbo:20170803012705p:plain

「共有」ウィンドウで「リモートログイン」を有効に設定します。

f:id:daigo-knowlbo:20170803012732p:plain

これにより Visual Studio 2017側 から Mac側 へのアクセスが可能になります。
(Macとの接続がとられていないと、iOSアプリの実行・デバッグが行えません)

1.1.2 Visual Studio 2017からの接続設定

Visual Studio 2017のメニュー「ツール → iOS → Xamarin Mac Agent」を選択します。
「Xamarin Mac Agent」ウィンドウが表示されるので、「次へ」をクリックします。

f:id:daigo-knowlbo:20170803012839p:plain

Macが同一ネットワーク上に存在すればリストに表示されます。
接続したいMacを選択して「接続」ボタンをクリックします。

f:id:daigo-knowlbo:20170803012929p:plain

ログイン情報を入力して「ログイン」ボタンをクリックします。

f:id:daigo-knowlbo:20170803013024p:plain

以上で、Visual Studio 2017からMacへの接続設定が完了しました。

1.2 Androidエミュレータ実行環境設定について

初期設定として行うべきことが多く躓きやすいと思います(私もXamarinやり始めのころかなり躓きました・・・)。
まず前提として、Androidエミュレータは HAXM(Intel Hardware Accelerated Execution Manager) を使って実行します。そうでないと使い物にならないほど激遅です。
で、HAXMを動作させるには「Hyper-Vを無効化」しておかなければなりません。

1.2.1 Hyper-Vの無効化

Windowsの機能として「Hyper-V」をインストールしていなければ気にする必要はありません。
Hyper-Vがインストールされている場合には、以下のコマンドによりHyper-Vを無効化します。

bcdedit /set hypervisorlaunchtype off

※コマンド実行後は、再起動により設定が反映されます。

逆にHyper-V機能を有効化するには以下のコマンドを実行します(要再起動)。

bcdedit /set hypervisorlaunchtype auto

1.2.2 Google APIs Intel x86 Atom System Image と HAXM のインストール

エミュレータイメージである「Google APIs Intel x86 Atom System Image」と、「HAXM」をインストールします。
Visual Studio 2017のメニューから「ツール → AndroidAndroid SDK マネージャー」を選択します。

f:id:daigo-knowlbo:20170803013723p:plain

表示された「Android SDK Manager」から、以下を選択して「Install xx packages..」ボタンをクリックします。

f:id:daigo-knowlbo:20170803024247p:plain
f:id:daigo-knowlbo:20170803024306p:plain

HAXMの方は「Not compatible with Windows」と表示されてインストールできない場合があります。
その場合は以下から直接ダウンロードしてインストールを行います。

software.intel.com

zipファイルをダウンロードし、解凍して生成された「intelhaxm-android.exe」を実行することでHAXMのインストールが行われます。

f:id:daigo-knowlbo:20170803013951p:plain

1.3 UWP実行環境設定について

Windows 10の「スタートメニュー → 設定 → 更新とセキュリティ → 開発者向け」を選択します。
「開発者向け機能を使う」項目から「開発者モード」を選択します。

f:id:daigo-knowlbo:20170803014052p:plain

しばらく待つとインストールが完了します。

2 Visual Studio 2017 で Xamarin + Prism 開発

やっと本題です。

Visual Studio 2017を起動します。

2.1 「Prism Template Pack」のインストール

メニュー「ツール → 拡張機能と更新プログラム」を選択します。

f:id:daigo-knowlbo:20170803014331p:plain

拡張機能と更新プログラム」ウィンドウが表示されます。

「オンライン」を選択し、右上の検索ボックスに「Prism Template Pack」と入力します。
一覧に表示された「Prism Template Pack」を選択し「ダウンロード」ボタンをクリックします。
ダウンロードが完了したら、「閉じる」ボタンをクリックします。

f:id:daigo-knowlbo:20170803014426p:plain

Visual Studio 2017を終了させます。

「Prism Template Pack」のインストール画面が表示されます。

f:id:daigo-knowlbo:20170803014512p:plain

インストール画面の指示に従うことで、インストールが完了します。

f:id:daigo-knowlbo:20170803014526p:plain

2.2 Prismプロジェクトの作成

Visual Studio 2017を起動します。
メニュー「ファイル → 新規作成 → プロジェクト」を選択します。

f:id:daigo-knowlbo:20170803014613p:plain

プロジェクトテンプレート(カテゴリー)として「Prism」が追加されていることを確認することができます。

f:id:daigo-knowlbo:20170803014657p:plain

ここでは「Prism → Xamarin.Forms」を選択します。以下の4つのテンプレートがリストされます。

  • Prism Autofac App(Xamarin.Forms)
  • Prism Dryloc App(Xamarin.Forms)
  • Prism Ninject App(Xamarin.Forms)
  • Prism Unity App(Xamarin.Forms)

名称から分かるように、テンプレートごとに「利用するDIコンテナ」に違いがあります。
お好みで選択してOKですが、ここでは「Prism Autofac App(Xamarin.Forms)」を選択することにします。
名前はデフォルトのまま「PrismAutofacApp1」としました。
「OK」をクリックします。

「PRISM PROJECT WIZARD」ウィンドウが表示されます。

f:id:daigo-knowlbo:20170803014805p:plain

ターゲットとするプラットフォームの選択になります。ここでは iOS / ANDROID / UWP の3つすべてを選択します。

Macとの接続設定が未設定の場合は「Xamarin Mac Agentの指示」ウィンドウが開きます。
ネットワーク内に開発に使えるMacがある場合は接続します。
Macとの接続設定が済んでいる環境では、このステップは省略されます)

f:id:daigo-knowlbo:20170803015005p:plain

「新しいユニバーサル Windows プロジェクト」ウィンドウが表示されます。

f:id:daigo-knowlbo:20170803015130p:plain

UWPのターゲットバージョンの指定になりますが、ここではデフォルトのまま「OK」ボタンをクリックすることとします。

以上でプロジェクトの作成が完了します。

2.3 実行

では、ソースは何もいじらずに、ウィザードによって生成されたHello World的なプログラムを実行してみます。

2.3.1 Androidエミュレータで実行

VS上部のツールバー(?)から「スタートアッププロジェクト」を「PrismAutofacApp1.Droid」を選択します。
すぐ右のドロップダウンでシミュレータデバイスを選択します。ここでは「Visual Studio_android-23_x86_phone(Android 6.0 - API 23)」を選択します。

f:id:daigo-knowlbo:20170803020110p:plain

F5キークリックでデバッグ実行を行います。
ビルド&デプロイが行われ、以下のエミュレータ画面が表示されます。

f:id:daigo-knowlbo:20170803020132p:plain

※私の環境ではデプロイが完了しない場合があります。そんな場合は、一度ビルドのキャンセルを行い、再度F5をクリックするとうまく動作したりするようです。

2.3.2 iPhoneシミュレータ(iOS)で実行

VS上部のツールバー(?)から、「ソリューション プラットフォーム」を「iPhoneSimulator」に、「スタートアッププロジェクト」を「PrismAutofacApp1.iOS」に設定します。
すぐ右のドロップダウンでシミュレータデバイスを選択します。ここでは「iPhone 7 iOS 10.3」」を選択します。

f:id:daigo-knowlbo:20170803020549p:plain

F5キークリックでデバッグ実行を行います。
ビルド&デプロイが行われ、シミュレータ画面が表示されます。

Visual Studio 2017の「リモート Simulator から Windows へ」機能が有効になっている場合は、Windows側の画面にシミュレータが表示されます。この機能が無効になっている場合には、Mac側にシミュレータ画面が表示されます。

[Windows側にシミュレータ表示]
f:id:daigo-knowlbo:20170803022544p:plain

[Mac側にシミュレータ表示]
f:id:daigo-knowlbo:20170803022450p:plain

「リモート Simulator から Windows へ」機能の設定変更は以下の通りです。
Visual Studio 2017のメニュー「ツール → オプション」を選択し、「オプション」ウィンドウを開きます。

f:id:daigo-knowlbo:20170803022835p:plain

左側のカテゴリーから「Xamarin → iOSの設定」を選択します。右側に表示される「リモート Simulator から Windows へ」チェックボックスで有効・無効の設定を切り替えます(当該機能はVisual Studio Enterpriseのみの機能になります)。

2.3.3 UWPで実行

VS上部のツールバー(?)から、「ソリューション プラットフォーム」を「x64(もしくはx86)Any CPU」に、「スタートアッププロジェクト」を「PrismAutofacApp1.UWP(Universal Windows)」に設定します。
すぐ右のドロップダウンでシミュレータデバイスを選択します。ここでは「ローカル コンピューター」を選択します。  

f:id:daigo-knowlbo:20170803074041p:plain

F5キークリックでデバッグ実行を行います。
以下のようにUWPアプリが起動します。

f:id:daigo-knowlbo:20170803023310p:plain

※2017/8/3 かずき@69.7kg (@okazuki) | Twitterさんより「ソリューションプラットフォーム」を AnyCPU ではなく x64 もしくは x86 を明示的に選択すれば「配置」の必要がないことをご指摘いただきました。「2.3.3 UWPで実行」に関する 以下の取り消し線部分を上記に修正しました!かずきさんありがとうございます!

配置→実行の手順となります。

(1) 配置

ソリューションエクスプローラから「PrismAutofacApp1.UWP」を選択し、マウス右ボタンクリックでポップアップメニューを表示、「配置」メニューを選択します。

f:id:daigo-knowlbo:20170803023005p:plain

ビルドが行われ、配置処理が行われます。

(2) 実行

VS上部のツールバー(?)から、「ソリューション プラットフォーム」を「Any CPU」に、「スタートアッププロジェクト」を「PrismAutofacApp1.UWP(Universal Windows)」に設定します。
すぐ右のドロップダウンでシミュレータデバイスを選択します。ここでは「ローカル コンピューター」を選択します。  

f:id:daigo-knowlbo:20170803023228p:plain

F5キークリックでデバッグ実行を行います。
以下のようにUWPアプリが起動します。

f:id:daigo-knowlbo:20170803023310p:plain

まとめ

ということで、コーディング0の「Xamarin + Prism 超入門」でした。
Xamarinまわりは、以前と比べてかなり改善されていますが、初期設定関連の取り掛かりの部分が躓きとなる可能性がある部分だと思います。
ということで、Xamarin + Prism開発、更にはその手前の初期設定に関して少しでも本エントリーが参考になればと思います。