そういえば転職してました・・・

転職しました

最近(ここ数ヶ月も・・・><)技術ブログを放置してしまっていたのですが・・・そして久しぶりの この投稿も技術ブログではないのですが・・・
こんなところで「転職のご報告」をしておこうと思います。
えーーと、2017年12月末で前職を退職し、2018年1月より新たな職場に勤めております。
ただし、前職の残務と並行する形で新たな職場での仕事を始めるという、ちょっと変則的な状況でありまして、関係各所・皆様には正式なご挨拶等出来ないまま、2月を迎えました。。。という状況です。

理想の仕事

転職に関しては、初老を迎えてしまった私ではありますが、以下のような不安・欲求を持っていました。そして、モヤモヤもしていたかも・・・

  • 個人的に欲する年収
  • もう おっさん だし新しい環境とかどうなんだろう・・・
  • でも、toC なスケーラブルなサービスに関わりたい
  • チャレンジングなサービスに関わりたい
  • そして、管理職ではなくて技術者として生きていきたい
  • でも、管理を放棄するわけではない・・
  • なんか「面白い」環境に身を置きたい
  • 結局 技術が大好きだよ

そう・・昔ながらの30超えたら管理職、コードを書いていたら年収上がらない、というトラディショナル環境は肌に合わず、エンジニアとしてチャレンジングな環境を求めていました。
そして、立ち止まってしまう世界も嫌だ、と。

転職ドラフト

20代の自分であれば、年収の足かせも無く、やりたいことができる場所へ、ある意味簡単に飛びつけたと思いますが、生活に対して守りの自分がいたのは事実・・・
そんな中「転職ドラフト」という存在を会社の同僚から知りました・・・
なんかエモーショナルで楽しそう!
そう、私は(見た目は地味だけど)、安定よりも「エモーショナルな事」「楽しそー!な事」が大好物です。
そして早速登録!(開催期間の関係上、1クール待った感じかな・・・)
ドラフトは、一般的な 面白みのない(?)(つらつらとした)職務経歴書を書かなくて良いのも、むしろ、好感的。
なんか、転職活動が楽しい、といった印象でした。
なんだかんだ、いくつかの魅力的な企業からアポイントを受けることができました。
名の知れた企業から 現年収+α のオファーを頂けたり、現年収-α のオファーを頂けたり・・・印象としては「あー、もっと早く動いていれば良かった」かな・・

NewsPicks (株式会社ニューズピックス)

そして結局、私は「株式会社ニューズピックス」に入社する事を決めました。
決して、単純な金額面のオファー条件は一番ではありませんでしたが(実は目先のお金だけでいったら freee さんが一番好条件だったかも・・・)初期アポイントメントから、1度のカジュアル面接、2度の採用面接、オファー面談、等々・・・トータルで魅力を感じることができました。
NewsPicksは、経済情報にフォーカスしているビジネスマンの為のサービスであり、どちらかというと、エンジニアにはなじみが薄い可能性があります(実は私もオファーを受けるまでほとんど知らなかったという・・・)

また、月額1,500円という、普通の toC サービスとしては一見 割高感を覚えるサービスですが、落合陽一さんの放送をライブストリーミングで毎週見れたり、東洋経済から移籍してきた佐々木紀彦編集長率いる自社編集部による質の高いニュース記事が読めたり・・・社員となった私が言っても説得力が低いかもしれませんが、かなり面白いコンテンツを得ることができます。
最近だと仮想通貨関連に興味を持っている方には、有益なコンテンツが多数あると思います。

MicrosoftJava(OSS等) / Azure → AWS

そう、今回の転職によって、仕事で使用する 技術スタック も大きく変化しました。

言語は C#Java(Kotlin等含む)
クラウドインフラは Azure → AWS

ここ数年、クラウド技術やOSSフレームワーク等は「マイクロソフトだから」とか「OSSだから」とか、そんな垣根は薄くなり融合しています。とはいえ、ある程度の垣根はあり、その壁を超えるには少しの努力は必要となると思っています。
そんな意味で、(決して超えられない壁ではないけれど)、MS→MSであれば帰宅後に酒を飲んで過ごしても行けるところが、少しの学習努力を要する状態になったかと思っています。 しかし、これは結局、新しい領域を求める私にとっては楽しい苦しみでしかないので、結局楽しいのですが。

まとめ(?)

そう、結局、新たな世界で楽しむことにしました!
転職活動中から年末年始で私自身の生活が わさわさ してしまい諸々停滞してしまいましたが、勉強会 だったり もくもく だったり・・・LT や 登壇 など、今年からはもっと積極的に楽しんでチャレンジしていきたいと思っています。
そして Azure も AWSもウォッチしたいし、.NET も Java界隈 もウォッチしたいなぁ・・・と。
ということで、そんなチャレンジを宣言するブログということで!
今年も皆様よろしくお願いいたします!

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つであると思います。