xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

CloudStructures v2.1.0 released!!

前回に引き続き今回も CloudStructures ネタです。.NET Standard 対応を行ってから来たフィードバックにお応えしたのと、反省点の修正 (?) を行いました。変更点は大きく 3 点あります。

  • RedisLock 型の追加
  • 非同期メソッドとして提供しているコマンドに Async 接尾詞をつける
  • IRedisStructures から DefaultExpiry プロパティを切り出す

RedisLock 型の追加

Grani 時代に同僚のみっちぃ (@mitchydeath) から「DistributedLock を作れない」と言われました。完全に見落としてました...。

ということで、それを可能にするためのコマンド群を RedisLock として追加しました。いい感じにラップすれば C# 8.0 で提供されそうな IAsyncDisposable にも対応できるんじゃないかと思います。

命名規則の変更

これまで CloudStructures は Redis コマンド関連のメソッドに Async の接尾詞をつけていませんでした。非同期メソッドだけど!これは昔の @neuecc さんの blog に書いてありますが、「悩んだけど消した」という歴史的な理由に依ります。

ですが「やっぱり付けた方がいいよね」ということで、破壊的変更 MAX ではありますが付けて回りました。非同期メソッドには Async を付けましょう!

IRedisStructureWithExpiry を新設

RedisStringRedisLock のようなコマンド型は基底インターフェースとして IRedisStructure を持っています。これまでそこに DefaultExpiry プロパティを持っていたのですが、RedisLockRedisLua のような型では DefaultExpiry プロパティを利用していないという現実がありました。これはプロパティを持っているだけ無駄なので、DefaultExpiry プロパティを持っているインターフェースと持っていないインターフェースに分離しました。

public interface IRedisStructure
{
    RedisConnection Connection { get; }
    RedisKey Key { get; }
}

public interface IRedisStructureWithExpiry : IRedisStructure
{
    TimeSpan? DefaultExpiry { get }
}

public readonly struct RedisLock<T> : IRedisStructure{}
public readonly struct RedisString<T> : IRedisStructureWithExpiry{}

これも十分に破壊的変更ですが、あるべき姿により近づくと思って導入しました。

CloudStructures now supports .NET Standard!!

Grani 時代から Redis 操作ライブラリとして長く愛用してきた CloudStructures ですが、残念ながらこれまで .NET Core に対応していませんでした。ただ、こればっかりはさすがの @neuecc 先生もあれもこれもメンテするのは難しいので仕方ない!.NET Core 対応済みの王道ライブラリは StackExchange.Redis だと思いますが、余りにプリミティブ過ぎて使い勝手が良いとは言えません。もはや CloudStructures がないと生きていけない体にされてしまった...(罪深い

f:id:xin9le:20190228223150p:plain

と言うことで意を決して .NET Core 対応をしようと思い立ち、2018 年 6 月頃から勝手に Fork (コピー) してヒッソリと作業を始めました。半年以上プライベートで使っていたのですが、.NET Core 3.0 のリリースも近づいている今、より盛り上げるためにも本家に入れてもらえるように @neuecc 先生に話をして承認いただき、ついに正式に .NET Standard 版となりました!

主な変更点

使用感としてはそこまで変わらないのですが、以前のバージョンと比べるとかなりの破壊的変更が加わっています。リリース当時から比べると Managed な Redis でクラスター環境が提供されるようになったり、依存していたライブラリやフレームワークの環境も相当に変化していて、そういった補完していた部分だったりを今風に適応/アレンジしました。また、近年は C#/.NET もパフォーマンス最適化が命題となってきているので、その辺りにも最大限気を配ってみました。

リファクタリング系

  • RedisSettings / RedisGroupRedisConnection に一本化
  • RedisString に含まれていた Bit 関連のコマンドを RedisBit として切り出し
  • Geo 関連コマンドを RedisGeo として新設
  • RedisClass を削除
    • Grani の中でもほとんど使われてなかった
  • RedisSubject を削除
    • これは元々別パッケージとして提供されていたので一旦削除
    • 今後対応を考えるかも
  • IServerSelector を削除
    • 自前でロードバランスするのではなく Cluster 化された Managed Redis を使う方が時代に即してそう
  • TraceHelper を削除
  • app.config / web.config からの設定読み込み機能を削除
  • Glimpse サポートを削除
    • Glimpse が .NET Core に対応していないため

パフォーマンス改善系

  • RedisString などを class から struct に変更
    • ほとんどのケースでインスタンスの生存期間が非常に短くスコープも狭いため、ヒープアロケーションを避けるべきと判断
  • ラムダ式の変数キャプチャを完全に排除
  • ボックス化を完全に排除
    • 特に ValueConverter 周りの仕組みを思い切り改善

まとめ

CloudStructures を隅から隅まで全部読んで書き換えたこともあって、だいぶ理解が深まりました。パフォーマンス改善関連もすでに世の中で実現されているテクニックのいくつかを真似しているだけですが、そういったところに細心の注意を払うってすごく地道だし大変って実感しました。ほんと、すごく勉強になりました。

あとライブラリの Contributor にしていただいたので、何かあれば Pull Request などいただければ!.NET で Redis を扱うときには是非 CloudStructures を使ってみてくださいね!

平成最後なのでソーシャル就活で転職してみた

「平成最後なので」というのは何の意味もない枕詞ですが、今風な感じ (?) を出すためだけに付けてみました。それ以上の理由はありません!タイトルの通り今回ソーシャル就活というのもをやってみたのですが、どんな感じだったかを書いてみようと思います。

f:id:xin9le:20190203190632p:plain

ソーシャル就活という選択

今回退職に伴って、次の就職先をどうやって探そうかと悩んでいました。これまで転職先を探すとなったとき、大きく 3 パターンほどあったように思います。

  1. 転職/求人サイトの活用
  2. 転職エージェントに紹介してもらう
  3. 知人の会社にアプローチする

僕自身これまでの転職は「3」しか行ったことがなく、求人サイトや転職エージェントを利用したことはありませんでした。求人サイトを活用するのは重要な方法だとは思いますが、無数にある企業の中から、しかもどれも似通った募集要項の中から自分自身のスキルセット / やりたいこと / 希望年収レベルにマッチした条件を探し出す難しさはかなりのものだと思います。そして何よりも必死に探し当てた企業様が本当に自分を必要としてくれているのかどうかがどうしても分からない、という辛さがあります。(必要とされていない場合は落とされるだけだと思いますが...)

そう考えたとき「そもそも自分は市場に求められている人材なのか?」「そもそも市場価値があるのか?」という疑問でいっぱいになり、それを確認してみたいという気持ちが強くなりました。最近少しずつ #Twitter転職 というハッシュタグも見かけるようにもなりましたし、SNS を利用して転職活動中であることをアピールしてみるのもありかもしれない。そうすると自分の市場価値も分かるかもしれない。RT もいいねもされなければ拡散されないのでその点はかなり運なのですが、一切声がかからなければ市場価値なしと判断できます。そうすれば生き方 / 活動の仕方を改める必要があると分かるので、それもまたよい仮説検証だろうと前向きに捉えてやってみることにしました。

レジュメの作成

ただ単純に「仕事探してます!声掛けてください!」だけで反応してくれるとは全く思わなかったので、自分のスキルセットや経験 (= いわゆる職務経歴書) を見せて僕自身を知ってもらう必要がありました。なので GitHub にレジュメを作って公開しました。これは Grani 時代の仲間である @neuecc さんや @guitarrapc_tech さんがやっていたので、素晴らしい方法だなぁと思って真似させてもらいました。

あれもこれも文章だと読んでもらえないので、スキルセットをキャッチーにイイ感じに表現できないかなぁと考えて☆で表現してみました。自分が持っている技術とその理解度を知るだけでなく、改めて弱点を把握する時間にもなりとても有意義でした。これは職務経歴書を作るでなくてもやってみて損しないと思います。レジュメ作って本当に良かった。

反響

Twitter / Facebook の両方に「転職先探してます」の投稿をしました。Facebook は友達限定公開の投稿なのでお見せすることができませんが、Twitter は以下のような感じで投稿しました。

大変ありがたいことに多数の RT といいねをいただきまして、ツイートアクティビティは (執筆時点で) 概ね以下のような感じになっています。職務経歴書を 2640 回も見てもらえるなんて普通に就活してたら絶対無理

f:id:xin9le:20190203185447p:plain

たくさんの方の目に触れる結果となったことで、自分が把握してる範囲で大小合わせて 33 もの個人/企業様からお声がけいただきました。もし自分で会社探しをしていたとしてもこんなにたくさんの候補先を見つけることは絶対に無理でしたし、逆にこんなにも自分を必要としてくれている企業様があるんだと知って自信がつきました。僕の周りには「遠く及ばない」と思う尊敬できる方々がたくさんいますし、おかげで劣等感に苛まれることも多々あるのですが、この結果は本当に気分を上向かせてくれました。

一晩にして 30 近くの連絡があったのですが、とりあえず返信をするのにとんでもなく時間がかかりました…。A さんに返事を書き、続けて B さんに返事を書いている間にまた A さんからの返信が来るみたいな感じで対応に追われまくり、6 時間かかっても全員に返事をすることができませんでした。話をしていくと多くのケースで「一度食事でもしながらお話しませんか?」という流れになるものです。そんなこんなで 1 月中旬から 1 月末まで毎日誰かと食事するという経験したこともないものすごいスケジュールになってしまいました。飲み会以外での飲酒は一切しないので、体が持つか正直不安でしたw

余談

おまけですが、ディライトワークスさんにお声がけいただいた事が 2ch まとめに載りました。さすがの大人気ゲーム FGO*1 (= Fate/Grand Order) 効果!載っていることを友人から教えてもらったのですが、まさか拾われると思ってなかったのでさすがに笑ってしまいましたw

転職先の決定

身に余るほどたくさんの候補先が見つかりましたが、有名どころで言うと以下のようなところがありました。なんというかネームバリューだけでも圧倒されてしまいますね...。

今回僕は転職における第 1 条件として「リモートワークを許可してくれること」を掲げていたので、それが理由で多くのところが条件から外れていきました。「そういうのは最初に書けよ」と言われそうですが、希望年収 / 希望配属先 / 希望役職などと同様に求める条件のひとつでしかないと思っていたので書きませんでした。現在 3 年半ほど東京に単身赴任をしている身なのですが、この期間は家族と過ごす時間とのトレードオフとしてエンジニア人生を充実させてきました。これは悪くない選択だったと思っていますが、その分今度は家族との時間をこれまでより多く確保したいという気持ちが強くなり、それがリモートワークを求めた理由です。

そんな僕のある種のワガママのようなライフスタイル / ワークスタイルを受け入れてくれた会社さんが数社あり、その中でも以下の 3 社でかなり迷いました。必須条件をクリアしたあとは年収レベル / その会社で体得できること / 福利厚生などのバランスで決定することになりますが、どの会社さんも甲乙付けがたいくらいに素晴らしかったです。

これまでのキャリアではデスクトップアプリから Web アプリ、XR アプリまで幅広く手がけてきましたが、クラウドインフラの構築はそこまで詳しくなく (できないわけではないけれど) まだ得意と言えるほどではありません。特定のクラウド信者ではないので Azure / AWS / GCP などどれでも特に抵抗はないのですが、クラウド領域のスキル強化は今後のエンジニア人生として重要なものになると考え、今回は Azure の分野で最前線を走る会社のひとつであるシグマコンサルティングさんを選択することにしました。社長の橋本さんが僕のワークスタイルや今後の目標に共感してくれたことも、大きな決め手になりました。

長々と書いてきましたが、今回のソーシャル就活は拡散運にも恵まれつつ、個人的には非常に良い経験/結果になったと思っています。お声掛けくださった皆様、本当にありがとうございました!

おまけ : ソーシャル就活するまで 1 年間の活動

1 年前の今頃は株式会社グラニというゲーム会社に勤めていたのですが、諸般の事情で事業売却することとなり、それに伴って 2018 年 4 月より MYNET グループ配下の株式会社 GMG に承継転籍していました。そこも 2018 年 6 月で退職し、以降は知人のスタートアップ企業に勤めていました。そこではリードエンジニアとして以下のような多岐にわたる活動をしていました。

  • コアライブラリの作成
  • CI などの開発インフラの整備
  • クラウドインフラの整備
  • アプリケーション実装
  • デザイン/サービス仕様の検討
  • チームビルディング
  • 技術選定
  • etc...

このときの転職先としてスタートアップ企業を選んだ理由は、ひとり当たりに占める責任の割合の大きさを重視することでした。仕事における責任範囲とは会社規模が大きくなるにつれ明確化されていくもので、そういったある程度の規模のチームに join するとほぼ確実に個々人の能力が最大化されそうな場所に配置/配属されるものです。これは採用側として何ひとつ間違っていない采配ですが、ことスタートアップのような少人数チームとなるとそうは行かず個々人の責任範囲が必然的に広くなります。当時の僕は「自分の weak point (= 未経験な技術領域) を少しでも業務を通じて克服したい」という気持ちが強く、そのためなら投資を惜しまないという考えでした。

ここで言う投資とは時間的な投資と金銭的な投資の両方の意味を含みます。スタートアップは半年 ~ 1 年で芽を出せるかどうかが肝心です。極めて短期勝負。その期間広い責任を持ちつつ全力で突っ走ることは否応がなく短期間でスキルを伸ばすことに繋がる、と考えていました。またそのような経験を得られるのであれば金銭的にそこまで恵まれなくても、たとえ毎月赤字になろうとも、十分価値のあるものになると考えていました。実際ひとりの娘とパートをしている妻がいる身ではありますが、たとえ僕が 1 円も稼がなくても数年生きることができる程度には貯金していたので、短期的な勝負であれば金銭的にも問題ないと判断してスタートアップに join することにしました。それでスタートアップ事業が成功すれば +α で win です。

結果としてはこの思惑はほぼ計画通りに進み、僕は実質 8 ヵ月程度の在籍ながらかなりの経験を積むことができたと思います。残念にも事業の方向性がブレ続けて定まらなかったため、ずっと付き合うことが技術的にも金銭的にも自分の win にならないと判断して退職することにしました。事業の成功という +α を得ることができなかったのは残念ですが、スタートアップ企業に実際に勤めてみてその難しさを直に体感できたこともまた良い経験でした。

*1:Apple Store で年間 1000 億円売り上げたモンスタータイトル

キャリッジリターン (CR) を無視する ModelBinder を適用する

タイトルの通りです、それ以上の情報がないのですが...!下記のドキュメントを参考に、そういうものを作りました。

動機

iOS の Safari から改行を含む <textarea> のデータを POST すると勝手に CR が付与されるという問題がありました。Form の Submit イベントを JavaScript でハンドリングして CR を除外してから送信しても勝手に付与される不思議!なんでだ!

もはやクライアント側では回避不能なのではないかということになり、サーバー側で CR を削除するしかないということで対応することにしました。

実装

ASP.NET Core MVC には (ASP.NET MVC にも) ModelBinder という POST されたデータをプロパティや変数に値を詰める処理をカスタマイズする拡張ポイントがあるので、それを使っています。案外簡単ですね!

public class IgnoreCarriageReturnStringBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        //--- 値を取り出す
        var modelName = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
        if (valueProviderResult == ValueProviderResult.None)
            return Task.CompletedTask;

        //--- ModelState を更新
        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        //--- CR を削除
        var value = valueProviderResult.FirstValue;
        if (value != null)
            value = value.Replace("\r", string.Empty);

        //--- バインディング成功
        bindingContext.Result = ModelBindingResult.Success(value);
        return Task.CompletedTask;
    }
}

あとは適用したいプロパティにピンポイントで属性を貼れば OK です。サービス全体で適用する場合は Startup.cs で処理しても良いですが、良し悪しあるので適宜判断してください。

public class HogeModel
{
    //--- textarea とマップされるプロパティ
    [ModelBinder(typeof(IgnoreCarriageReturnStringBinder))]
    public string Text { get; set; }
}

Microsoft Connect(); 2018 Japan で登壇しました & Visual Studio 2019 新機能フォローアップ

Microsoft Connect(); 2018日本版 Wrap Up イベントに参加し、ちょまど (@chomado) ちゃん枠のトップバッターとして Visual Studio 2019 (Preview) の新機能について登壇/解説させていただきました。いろいろドタバタしつつもとても楽しかったですw

イベントは YouTube Live でストリーミング配信されていて、アーカイブも残っているのでいつでも見直すことができます。僕の担当部分は 42:40 あたりから。また、デモで利用していたプロジェクトは GitHub で公開しています。

解説した機能

イベント中に解説したのは以下の機能です。10 分ちょっとのデモにしてはだいぶ詰め込んだ感がありますが、こんなことができるようになったというのを上記動画から少しでも感じていただけていれば幸いです。

  • Look & Feel の変更
  • Document Health Indicator
  • Code Clean-up
  • .editorconfig のエクスポート
  • Convert to LINQ
  • IntelliCode

ちなみに Microsoft 公式の What's New 解説は以下にあります。

解説すれば良かった機能 / し忘れた部分

「やればよかった」「言い忘れた」というものももちろんあります。フォローアップとして以下の 2 つを取り上げて解説します。

IntelliCode の導入方法

デモでは「あたかも IntelliCode が Visual Studio 2019 に標準搭載されている」ような説明をしてしまいました。これは間違い & とても反省していて、IntelliCode は Visual Studio の拡張機能としてインストールする必要があります。

f:id:xin9le:20181223204732p:plain

Marketplace サイトからダウンロードしていただいても大丈夫です。

また IntelliCode は Visual Studio 2019 からの限定機能ではないので Visual Studio 2017 でも利用することができます!業務で Visual Studio 2017 を利用している方は、今すぐ IntelliCode で検索!

デバッガーの強化

Visual Studio 2019 ではデバッグ機能がまたひとつ強化され、ウォッチウィンドウで変数や値の検索ができるようになりました!これを紹介しなかったことを後悔しています。超便利!(語彙力

f:id:xin9le:20181223205411p:plain

これまである時点で特定の変数に特定の値が入っているかどうかを調べようと思ったら、僕の知る範囲では以下の 2 つしか方法がありませんでした。

  1. ウォッチウィンドウで変数を掘り返して特定の値が入っているかを確認する
  2. 条件付き Break Point で止める

Visual Studio 2019 からは Break Point で止めてから検索することができるようになるので、非常に捗るのではないかと思います。

ちょっとした裏話

事前に僕に割り当てられていた時間は 12 分でした。なのですが会場のディスプレイに Mac が軒並み接続できないというトラブル *1 があって、そのせいで開始が 4 分も遅れてしまいました。一応そんなこともあろうかとバックアッププランとしてちょまどちゃんの Surface Pro にもデモ環境を準備しておいてよかった...。

だけどそもそもカツカツの 12 分!という中で 4 分を失ってテンパる僕。普段から日本人らしく (?) 日本語キーボードを使っているのですが、ちょまどちゃんは英語キーボードを使っているのでミスタイプするわするわ...!そしてデモ中に隣でマイクを持ってもらっていたちょまどちゃんには、僕の手が震えていたのをしっかり目撃されていて...w

結果として僕が解説自体に使った時間は 11:30 だったのですが、接続トラブル込みで 15:30 を使ってしまったのでのっけからイベント全体のタイムスケジュールを崩す羽目になってしまい本当に申し訳なく...

関連記事 / スライド

*1:僕だけでなく他の方も接続が不安定だった