c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

path 依存型って何? 調べてみました!

qiita.comの 12/21 です。

先日社内の勉強會で Tour of Scala を見返してゐたら「path 依存型」と云ふ文言を見て、名前しか知らんなと思ひ返しました。そこで

path 依存型って何? どんなもの? 年齡は? 結婚してる? 調べてみました!

speakerdeck.com scrapbox.io

type selection

path 依存型 (path-dependent type) は、Scala の實裝者達による論文Nada Amin, Tiark Rompf, and Martin Odersky. 2014. Foundations of path-dependent types. SIGPLAN Not. 49, 10 (October 2014), 233–249.にて考察されてゐる、型の一つです。この論文では "type selection" と云ふ名で考察されてゐます。

x.L

この形の記號が目印なので、讀む時はこの形を目指してみてください。xが variable (變數) で、Lが型で、組み合はせてx.Lと云ふ型です。變數に依存した型、と云ふ訣で path「依存」型と呼ぶのでせう (依存型 (dependent type) の一種と呼べん事は無い…。實際、path 依存型と關聯して Scala 3 から導入された依存函數型 (dependent function type) は依存型の一種です)

type selection がどう導入されるのかは、論文末尾の式を鑑賞していただく事として、ここでは path 依存型の使ひどころを眺めてみませう。

path 依存型

path 依存型は、class や trait の內側で定義した class や trait の instance の型として登場します。

docs.scala-lang.org

class A {
  class B {
    def f(b: B): Unit = {}
  }
  def newB: B = new B()
}

val a1 = new A()
a1.newB.f(a1.newB) // OK

val a2 = a1
a1.newB.f(a2.newB)
// Found:    Playground.a2.B
// Required: Playground.a1.B
a2.newB.f(a1.newB)
// Found:    Playground.a1.B
// Required: Playground.a2.B

class A の內側で定義した class B の instance の型は、class B の instance を生成する時に使った class A の instance に依存します。a1.newB の型は a1.B であり、a2.newB の型は a2.B と、區別されます。變數 a1 と變數 a2 は、val a2 = a1 であり同じ値ですが、path 依存型は値ではなく變數に依存するので、class B の instance の型は區別されます (nominality (名前的な型) と general refinements (總稱細別型 (「細別型」は大抵「篩型」と譯されますね)) とを考慮してとの事だと論文にはあります)

path 依存型は、型射影 (type projection) による型 A#B に cast でき、cast すると同じ型の値として扱へる樣になります (A#B として振る舞はせると Java っぽくなりますね)

何に使ふの?

さて使ひどころです。

多くの記事には、tree の appendChild 先を閒違へない樣にする例を見ましたが、そんな狀況って多いんでせうか?

面白かったのはPath dependent types. Modeling algebraic structures has never… | by Marcin Rzeźnicki | VirtusLab | Mediumの例です。2 進數の演算體系と 10 進數の演算體系を作って、それぞれの中では演算できますが、2 進數の數と 10 進數の數を演算しようとすると compile error にできます。

case class Z(modulus: Int) {
  sealed class Modulus {
    val value = modulus
  }

  object Modulus {
    implicit val mod: Modulus = new Modulus()
  }
}

class IntMod[N <: Z#Modulus] private (val value: Long) extends AnyVal {
  def +(x: IntMod[N])(implicit m: N): IntMod[N] = value + x
}

object IntMod {
  import scala.language.implicitConversions

  implicit def longAsIntModN[N <: Z#Modulus](i: Long)(implicit
      modulus: N
  ): IntMod[N] =
    new IntMod[N](i % modulus.value)

  implicit def intModN2Long(a: IntMod[_]): Long = a.value
}

val z1 = Z(2)
val z2 = Z(10)

val i1z1 = 1: IntMod[z1.Modulus]
val i2z1 = 3: IntMod[z1.Modulus]
val i1z2 = 1: IntMod[z2.Modulus]
val i2z2 = 3: IntMod[z2.Modulus]

println(i1z1 + i2z1) // 0: IntMod[z1.Modulus]
println(i1z2 + i2z2) // 4: IntMod[z2.Modulus]

println(i1z1 + i2z2)
// Found:    (Playground.i2z2 : Playground.IntMod[Playground.z2.Modulus])
// Required: Playground.IntMod[Playground.z1.Modulus]

他にはDependent Types in Scala - Some Tips, Tricks and Techniquesで、まるで普通の依存型かの樣に使ふ力強い使ひ方を紹介してあり、面白いものでした。

依存函數型

關聯する仕樣に Scala 3 から導入された依存函數型 (dependent function type) や dependent method (dependent method は Scala 2 から在ります) があります。

docs.scala-lang.org docs.scala-lang.org

函數や method の引數に登場した變數に依存した型を、他の引數や返り値の型として書けるものです。

trait A { type B; val b: B }
val f: (a: A) => a.B = (a: A) => a.b

函數 f の型が (a: A) => a.B となってゐる、a.B と云ふ path 依存型が含まれてゐるのが (Scala で言ふ) 依存函數型です。

總稱型みたいに使ふのが多いんでせうか。總稱型では、

case class Key[V](name: String) {}

class DB {
  def set[V](k: Key[V], v: V): Unit = {
    println(k)
    println(v)
  }
}

val db = new DB
val k1 = Key[String]("k1")
val k2 = Key[Double]("k2")

db.set(k1, "v1")
db.set(k2, 42.0)

と書くところは、依存函數型では、

abstract class Key(name: String) { type Value }
def key[V](name: String) = new Key(name) { override type Value = V }

class DB {
  def set(k: Key, v: k.Value): Unit = {
    println(k)
    println(v)
  }
}

val db = new DB
val k1 = key[String]("k1")
val k2 = key[Double]("k2")

db.set(k1, "v1")
db.set(k2, 42.0)

となりますね。set method の型 parameter が消えております。

いかがでしたか?

Mackerel で SAML を實裝してゐます #mackerelio

qiita.comの 12/18 (月) です。

Mackerel では今 SAML を實裝してゐます。提供時期や價格等はここには書けないのですが、實裝してゐる機能の內側を御覽に入れます。

想定讀者 :

  • Mackerel の SAML を導入する事を檢討する IdP 管理者
    • この記事は 2023/12 に實裝中の機能 (實裝豫定の機能を含みます) を紹介するものです。提供時にこの通りの仕樣である事を保證しません
      • 公式 document を意圖するものではありません
    • 將來提供されるであらう利用 guide では讀み取りづらい詳細を、檢討する役に立てば幸ひです
  • SP (service provider) を實裝するエンジニア
    • どちらかと言ふとこちらの讀者向きの文書です…
    • 既存の認證・認可 model と整合せせた一例として參考になれば幸ひです
    • SP を實裝するのに以下の本が大いに參考になりました

想定する讀者が讀者ですから、SAML の仕樣書に載る一般的な知識は解說しません。Mackerel ではどうしようとしてゐるかを紹介します。また Mackerel を一通り利用した事があるものとします。

現狀の認可 model

Mackerel と云ふ service は、SAML 規格では SP (service provider) に當たります。SAML は認證を提供する規格ですが、access 可否等結果的に認可にも影響します。

Mackerel の現狀の認可 model は以下の樣になってゐます。

(※以下全ての ER 圖は DB 上の實裝を表さず、槪念上の關係のみを表します。カラムの型も、Mermaid 記法の都合で省略できない爲に書いたもので、適當です。)

erDiagram

"オーガニゼーション" }o--|| "ユーザー" : "オーナー"
"オーガニゼーション" ||--o{ "メンバー" : ""
"ユーザー" ||--o{ "メンバー" : ""

"メンバー" {
    int authority_type "權限"
}
  • オーガニゼーションには複數のユーザーが所屬する
  • ユーザーは複數のオーガニゼーションに所屬する
    • オーガニゼーションは全ユーザーが自由に作れる
    • オーガニゼーションの管理者の意志で自由にユーザーを招待できる
    • 仕事のオーガニゼーションと個人活動のオーガニゼーション等、複數の組織のオーガニゼーションにユーザーは所屬できる。業務委託等でもこれは起こりうる
  • オーガニゼーション\timesユーザー毎に權限を管理する

ここに SAML を作用させます。

オーガニゼーショングループを導入する

オーガニゼーションは自由に作れる爲、一つの組織が多くのオーガニゼーションを使ってゐる事があります。或る IdP で認可されるオーガニゼーションを、一纏めにする槪念が必要です。

erDiagram
"オーガニゼーショングループ" |o--o{ "オーガニゼーション" : ""
"オーガニゼーショングループ" }o--|{ "ユーザー" : "オーガニゼーショングループ管理者"

"オーガニゼーショングループ" {
    varchar idp_entity_id "IdP の entityID"
    int idp_info "IdP の metadata 諸々"
}
  • オーガニゼーションはオーガニゼーショングループに屬する、または屬さない
  • オーガニゼーショングループには複數のオーガニゼーションが屬する
    • オーガニゼーショングループ は IdP の entityID と一對一對應する
    • オーガニゼーショングループには IdP の metadata を登錄する
    • オーガニゼーショングループに屬するオーガニゼーションへのユーザーの認可は、
      • オーガニゼーショングループによる認可を要求するか否か設定できる
      • 屬性マッピングを有效にするか否か設定できる
  • オーガニゼーショングループとオーガニゼーションの兩者の管理權限を持つユーザーは、オーガニゼーションをオーガニゼーショングループに屬させられる
    • 他人のオーガニゼーションを誤ってオーガニゼーショングループに屬させてしまふのを防ぐ爲

オーガニゼーショングループによって、SAML Response を檢證する口が IdP 毎に一つに纏まります。SAML を利用しないオーガニゼーションは、どのオーガニゼーショングループに屬さないかもしれません。

ユーザーはメンバーを介してオーガニゼーションに所屬すると云ふ點は變へてゐません。ユーザーはオーガニゼーショングループを跨いで、またはオーガニゼーショングループに屬さないオーガニゼーションにも參加できます。

SAML セッションはオーガニゼーショングループ毎に作成され、同時に複數持てる

Mackerel にサインインすると、勿論セッションが發行されます。しかし Mackerel に認證されてゐるからといって、オーガニゼーショングループに屬するオーガニゼーションを閲覽・操作できるとは限りません。オーガニゼーショングループに屬するオーガニゼーションに認可するか否かは、IdP を介して判斷しなければなりません。そこで IdP からサインインしたユーザーには、Mackerel のセッションに追加して、IdP 毎の SAML セッションを發行します。

erDiagram
"ユーザー" ||--o{ "SAML セッション" : ""
"オーガニゼーショングループ" ||--o{ "SAML セッション" : ""

"SAML セッション" {
    timestamp expired_at "有效期限"
}
  • 複數の IdP に同時にサインイン可能
  • ユーザーが見えるオーガニゼーションは、
    • 該當ユーザーがメンバーにゐるオーガニゼーションの內、
      • オーガニゼーショングループに屬してゐないオーガニゼーション
      • オーガニゼーショングループに屬してゐ、該當オーガニゼーショングループに關する SAML セッションを持ってゐるオーガニゼーション
        • 但しオーガニゼーションに例外承認 (下記) されてゐれば、對應する SAML セッションを持ってゐなくても見られる

ユーザーが複數の組織に屬する可能性を前提し、複數の IdP に對應する SAML セッションを同時に持てるのが、この實裝の特徴かと思ひます。GitHub 等が似た構造を持ってゐると推察してゐます。IdP に對應する有效な SAML セッションを持ってゐないと、IdP に對應するオーガニゼーショングループに屬するオーガニゼーションには認可されません。

屬性マッピング

屬性マッピングに對應するべく、IdP で設定した、SAML Response に含まれるユーザーの屬性を保存します。

erDiagram
"ユーザー" ||--o{ "IdP で設定したユーザーの屬性" : ""
"オーガニゼーショングループ" ||--o{ "IdP で設定したユーザーの屬性" : ""
  • 屬性マッピングを有效か無效か切り替へられる
  • IdP でユーザーに設定した屬性に從って、オーガニゼーション\times (參加不參加 + 權限) を強制できる
  • 屬性マッピングが適用されるのは以下の時

SAML でオーガニゼーションへの閲覽・操作を認可したとしても、オーガニゼーション毎に權限を管理するのは通常はオーガニゼーション管理者の役目です。しかし屬性マッピングを設定すれば、各オーガニゼーションへの參加不参加・權限も IdP で一元的に管理できます。

ユーザーを例外的に承認してオーガニゼーションへ認可する

業務委託や何らかの理由で、IdP には登錄しないが、しかしオーガニゼーションには認可したいユーザーが現れます。これらのユーザーを例外的に承認する必要があります。

erDiagram
"オーガニゼーション" }o--o{ "ユーザー" : "例外承認メンバー"
  • IdP に登錄せず、よって SAML ではサインインできないが、しかしオーガニゼーショングループに屬するオーガニゼーションの一部には認可したいユーザー
  • オーガニゼーショングループで別途指定する

IdP サインイン時の擧動

IdP からサインインし、ACS URL へ SAML Response を持ってユーザーが來た時の擧動は以下になります。

  1. SAML Response を檢證
  2. 對應するユーザーがゐなければ、JIT provisioning する
    • NameIDPolicy は emailAddress
  3. セッションが無ければ、セッションを發行する (Mackerel にも認證する)
  4. 對應するオーガニゼーショングループに屬するオーガニゼーション毎に、メンバーを調整する
    • オーガニゼーショングループによる認可を要求されてゐなければ、
      • オーガニゼーショングループに屬するオーガニゼーションに、
        • メンバーであればそのまま
        • メンバーでなければ、閲覽者權限で參加する
    • オーガニゼーショングループによる認可を要求されてゐれば、
      • 屬性マッピングが無效であれば、
        • オーガニゼーショングループに屬するオーガニゼーションに、
          • メンバーであればそのまま
          • メンバーでなければ、閲覽者權限で參加する
      • 屬性マッピングが有效であれば、
  5. SAML セッションを發行する
  6. 認可されれば (下記)、オーガニゼーションにアクセスできる

ユーザーが認可される範圍

或るオーガニゼーションを閲覽・操作できるユーザーは、以下の通りです。

  • オーガニゼーショングループに屬してゐなければ、
    • オーガニゼーションのメンバーにゐるユーザー
  • オーガニゼーショングループに屬してゐれば、
    • オーガニゼーショングループでの認可を要求してゐなければ、
      • オーガニゼーションのメンバーにゐるユーザー
    • オーガニゼーショングループでの認可を要求してゐれば、
      • オーガニゼーションのメンバーにゐるユーザーの內、
        • 對應する SAML セッションを持ってゐるユーザー
        • 例外承認されてゐるユーザー

オーガニゼーショングループでの認可を要求する樣にした時と、屬性マッピングを有效にした時には、認可の範圍が調整されます。

  • 對應する IdP で設定したユーザーの屬性を持ってゐないユーザーは、
    • 認可されなくなる
    • メンバー情報は維持される
    • 二要素認證を必須にした時の擧動に似る
  • 對應する IdP で設定したユーザーの屬性を持ってゐるユーザーは、
    • メンバー情報を適切に變更する
      • IdP サインイン時の擧動と同じ
      • 認可される樣になったり、認可されなくなったりする
  • 例外承認されたユーザーは、
    • 影響を受けない

以上となります。各方面に何か參考になれば幸ひです。それではよき SAMLai ライフを (?)

明日 Meetup を開催します。是非ゐらしてください。今からでも閒に合ふ! mackerelio.connpass.com

Ihaleakala Hew Len「たった 4 つの言葉で幸せになれる! 心が樂になるホ・オポノポノの敎へ」2009/9/13

私は人類の考へた事を collection するのを趣味としてゐるが、桃山の古本市場でこの本を捲ってゐて、

「アイスブルー」と言って植物に触れると、痛みに関する記憶のクリーニングを促すことができます。

と書いた一節を見附け、「壽司 虛空編」だ! と嬉しくなり買った (なにしろ安かった)。「壽司 虛空編」にはこの言葉が出てくるふざけた場面が在る。著者等の意圖には沿はぬだらうが、私にとってこの本はそれだけのものだ。

scrapbox.io

汎用おき

adventar.orgの 12/14 (木) です。

ここにエクストラバージンオリーブオイルが在ります。

エクストラバージンオリーブオイル

油です。ものを炒める時に引く油として使へます (香りが飛んで勿體無くはありますが)。頻繁に料理するのでもない限り、或いは揚げ物でも作らなければ、油は中々減りません。長期閒置かれると油も傷みます。傷まない內に使ひ切りたいものです。

ところでエクストラバージンオリーブオイルは、オリーブの實を加熱せずに破碎し遠心分離した果汁です。香り高く滋養の有る果汁 100% juice です。

エクストラバージンオリーブオイルを猪口に注いだ

飮めます。

料理用油としても使へ、パスタの香り附けにもなり、飮めば速やかに減ります。よかったですね。

と、ここで記事を終はりにしようかとも思ひましたが、これでは「おい」てはゐますが「つくっ」てゐません。つくりおきではありません。

炊飯

では米を炊いて保存してゆきませう。二合炊き、半合づつ冷凍します。

flowchart LR
米を洗ふ --> 浸水["米を 30 分〜1 時閒水に浸す"]
浸水 --> 鍋に入れて蓋をする
鍋に入れて蓋をする --> 沸騰する迄強火
沸騰する迄強火 --> 弱火にして泡が湧かなくなる迄
弱火にして泡が湧かなくなる迄 --> 蒸らす["10 分閒蒸らす"]
蒸らす --> 半合毎にラップに包み冷凍

米二合。

洗ひます。

水 400mL に漬け一時閒置きます。

鍋に入れ蓋をし、強火。

沸騰したら弱火。

水が米に吸われて見えなくなったら強火十秒。

火を止め、十分閒蒸らします。

炊き上がり。

四つに切り、

ラップに乘せ、

包みます。

四つ包み、冷凍します。

電子レンジで解凍すれば食べられます。

出汁

米が炊かれてゐれば料理が食事になります。出汁を引けば材料が料理になります。そこで昆布と鰹節の合はせ出汁を 2L 取りませう。

flowchart LR
昆布を2時閒浸水する --> 昆布を煮る["昆布を鍋に入れ、沸騰直前迄煮る"]
昆布を煮る --> 昆布を取り出す
昆布を取り出す --> 鰹節を煮る["鰹節を鍋に入れ、沸騰する迄煮る"]
鰹節を煮る --> 10分置く
10分置く --> 鰹節を取り出す
鰹節を取り出す --> 容器に移し冷蔵庫に入れる

昆布適量。鰹節 2g\times4 パック。

昆布を水に漬け二時閒置きます。

色。

昆布ごと湧かします。

沸騰する前に昆布を取り出します。色と泡。

鰹節を入れ、

湧かします。

沸騰したら火を止め、十分置きます。

色と濁り。

濾します。

冷藏庫に置き、二週閒以內に使ひます。溫めて味噌を入れれば飮めます。

出し殻は本當に始末に困ります。味はさっき抽出しましたので、これは無味。

昆布は切り刻み、鹽小匙一、味醂大匙一。

唐辛子が在ったので入れます。

水分が飛ぶ迄中火で炒めます。

姿。

ラップして冷藏庫に仕舞ひます。

翌日。昨日炊いて解凍した米、昨日取った出汁適量、味噌、卵。

米に餘熱が有るので、電子レンジで三十秒。混ぜます。

無味は無味。

明日は id:dstn18c さんです。

作ってよかった graceful shutdown ライブラリ

qiita.comの 12/12 (火) です。

去る 12/2 (土) に Go Conference mini 2023 Winter IN KYOTO で「作ってよかったgraceful shutdownライブラリ」と云ふ事を喋りました。

event report は以下。 scrapbox.io

ここでは喋った事を紹介します。

speakerdeck.com

はてなでは Go を (も) 使って application を作ってゐます。或る時「Go の code は記述量が多い」事が話題になりました。その中の一つに、graceful shutdown を實現するのに、似た code を每囘長々と書いてゐる事が擧がりました。そこで graceful shutdown の記述を改善できないか探索する事にしました。

graceful に起動・終了するとはどう云ふ事か? と云ふ一般論は省略しませう。slide には少し書きました。

滿たしたい條件は以下のものです。

  • signal を trap し、graceful に終了する
  • context.Context で cancel できる
  • 終了處理に timeout を設定できる
  • 任意の server を扱へる
    • batch server 等
  • 複數の server を扱へる
    • 一部の server が起動に失敗したら、process は graceful に exit 1 する
    • parallel に終了處理を行ふ
  • net/http server を簡單に扱へる
    • 記述を省略できる
    • 記述の形が明確である
    • http.ErrServerClosed を扱ふのを忘れない
  • gRPC server を簡單に扱へる
    • 記述を省略できる
    • 記述の形が明確である

以下の code で實現します。

package main

import (
    "context"
    "errors"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "time"

    "google.golang.org/grpc"
)

var shutdownTimeout time.Duration = 10 * time.Second

func main() {
    ctx := context.Background()

    ctx, stop := signal.NotifyContext(ctx, os.Interrupt) // signal を trap する
    defer stop()

    ctx, cancel := context.WithCancelCause(ctx)

    // net/http の例
    srv1 := &http.Server{Addr: ":80"}
    go func(ctx context.Context) { // parallel に起動する
        if err := srv1.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
            cancel(err) // 起動に失敗したら、異常終了する
        }
    }(ctx)

    // google.golang.org/grpc の例
    srv2 := grpc.NewServer()
    go func(ctx context.Context) { // parallel に起動する
        listener, err := net.Listen("tcp", ":8000")
        if err != nil {
            cancel(err) // 起動に失敗したら、異常終了する
            return
        }
        if err := srv2.Serve(listener); err != nil {
            cancel(err) // 起動に失敗したら、異常終了する
        }
    }(ctx)

    // 終了処理

    <-ctx.Done()
    if err := context.Cause(ctx); err != nil && !errors.Is(err, context.Canceled) {
        log.Fatalln(err.Error()) // 起動に失敗したら、異常終了する
    }

    ctx, cancelT := context.WithTimeout(context.Background(), shutdownTimeout) // context.Context を新たに生成する。終了處理に timeout を設定する
    defer cancelT()

    var wg sync.WaitGroup

    wg.Add(1) // parallel に終了する
    go func(ctx context.Context) {
        defer wg.Done()
        if err := srv1.Shutdown(ctx); err != nil {
            log.Println(err.Error())
        }
    }(ctx)

    wg.Add(1) // parallel に終了する
    go func(ctx context.Context) {
        defer wg.Done()
        stopped := make(chan struct{})
        go func() {
            srv2.GracefulStop()
            close(stopped)
        }()
        select {
        case <-stopped:
        case <-ctx.Done():
        }
    }(ctx)

    wg.Wait()
    if err := context.Cause(ctx); err != nil && !errors.Is(err, context.Canceled) {
        log.Fatalln(err.Error()) // timeout したら異常終了する
    }
}

…今見たら、timeout せずにしかし終了處理が error を返した場合に exit 0 してゐますね。まぁ結果的に作った library では、正しく exit 1 しますから氣にしない。

まづ既存の library を探しました。しかし設計が古かったり、net/http server 專用であったり、單一の server した扱へなかったりと、條件を充分に滿たすものは見附かりませんでした。先日話して、皆さん 1. graceful な shutdown を氣にしないか、2. 手書きするか、3. closed source な framework を作るかしてゐさうだと感じました。

見附からなければ作れば宜しいのですから、作りました。

github.com

先程の例は、かうなります。複數の server を扱ふので少し冗長ですが。

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/ne-sachirou/go-graceful"
    "github.com/ne-sachirou/go-graceful/gracefulhttp"
    "github.com/ne-sachirou/go-graceful/gracefulgrpc"
    "google.golang.org/grpc"
)

func main() {
    ctx := context.Background()

    srv := graceful.Servers{
        Servers: []graceful.Server{
            &gracefulhttp.Server{Server: &http.Server{Addr: ":80"}},
            &gracefulgrpc.Server{Addr: ":8000", Server: grpc.NewServer()},
        },
    }

    if err := srv.Graceful(ctx, graceful.GracefulShutdownTimeout(10 * time.Second)); err != nil {
        log.Fatalln(err.Error())
    }
}

大分短く成りました。

Mackerel の一部 system に導入した結果も slide に書きました。

同じ library を使ふ利點は、改善が容易くなる事です。

  • graceful shutdown の仕組みを作る者は、
    • この library を議論の場にできる
    • この library を改善すれば、改善した結果を波及させ易い
  • この library に依存する者は、
    • コピペする boilerplate が減る
      • コピペする code がよいものであるか否か調査する手閒が減る
      • コピペを續けても差異が產まれづらい
    • 依存 library の更新と、graceful shutdown の仕組みの改善を同一視できる。依存 library の更新は大抵は既に運用してますからね

よかったですね。

Erlang/OTP みたいな application 管理を實裝したくなりますが、抑へてをります (ぉ。

勿論 feedback を受け附けてをります。