HackToTech

Hack To Technology

semverが正しいかを確認する

仕事でタグの棚卸しをしていてこのsemver記法的に正しいんだっけみたいなのぱぱっと見る必要があったので備忘録がてら残しとく

オンラインでぱぱっと見れたらいいんだけど、
なんか探し方が悪かったのかsemverとは何かみたいなサイトばっかりひっかかったので
大人しくnpmのsemverパッケージ使う方が早そうだった
www.npmjs.com
package.jsonとか書いてるならnpm semantic version calculatorとかのほうが良い

# 適当に入れる(node.js入ってる前提)
$ npm install semver
$ node
> const semver = require('semver')
// ただ正しいかどうかならvalidでnullが返ってこなければOK
> semver.valid('1.0.0')
'1.0.0'
> semver.valid('1.2.3.4')
null


// どこがどれだっけ的なのを確認するならparseで見るのが手っ取り早かった
> semver.parse('1.0.0-beta')
SemVer {
  options: {},
  loose: false,
  includePrerelease: false,
  raw: '1.0.0-beta',
  major: 1,
  minor: 0,
  patch: 0,
  prerelease: [ 'beta' ],
  build: [],
  version: '1.0.0-beta'
}

Pythonで辞書の配列から新たに辞書を作成する

pythonのreduceたまにどこにあるかよくわかんなくて迷う

from functools import reduce

ld = [{'Key': 'Key1', 'Value': 'Value1'}, {'Key': 'Key2', 'Value': 'Value2'}, {'Key': 'Key3', 'Value': 'Value3'}]
reduce(lambda acc, x: dict(acc, **{x['Key']: x['Value']}) , ld, {})
# {'Key1': 'Value1', 'Key2': 'Value2', 'Key3': 'Value3'}

ld2 = [{'Key1': 'Value1'}, {'Key2': 'Value2'}, {'Key3': 'Value3'}]
reduce(lambda acc, x: dict(acc, **x), ld2, {})
# {'Key1': 'Value1', 'Key2': 'Value2', 'Key3': 'Value3'}

FargateでAWS CDKを実行して動的にクロスアカウントの別リージョンにデプロイしようとしたらハマった話

あんまりケースとしてはなさそうだけどCDKの実行基盤でFargateを採用してみた
なんでそんなことしてたかの詳細は割愛するとして、
自分同様DockerImageにCDKの諸々を固めて
cdk --profile なんたら --role-arn なんたら2 deploy
みたいなことをFargateでやっていて且つ他アカウントの別リージョンにデプロイする人の助けにでもなれば報われる

そもそも何が起こるか

他アカウントの別リージョンにデプロイするはずが、
他アカウントのデプロイ元のFargateが実行されているリージョンでデプロイがされる

例えば、Aアカウントの東京リージョンでFargateを立ち上げて
そこからBアカウントのオレゴンリージョンにデプロイとかしようとするとBアカウントの東京リージョンにデプロイされるという話

原因調査

上でdeploy時にprofileを指定している通り
~/.aws/config に↓のようなconfigを入れています
~/.aws/credentials は空ファイルにしてます

[profile xxx]
region = us-west-2
role_arn = arn:aws:iam::xxxxxxxxxx:role/DeployRole
role_session_name = deploy
credential_source = EcsContainer

環境変数にも似たようなものを入れてます
(というか↑のconfigを動的に生成する為に環境変数経由でデプロイ先のアカウントとリージョンをコンテナに伝えてます)

ACCOUNT_ID=xxxxxxxxxx
ACCOUNT_REGION=us-west-2

Fargate起動時にはプロセスにはこんな感じの環境変数が自動的に設定されています

HOSTNAME=ip-x-y-z-zz.ap-northeast-1.compute.internal
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/xxxxxxxxxxxxxxxxxxxxxxxxxxxx
AWS_DEFAULT_REGION=ap-northeast-1
AWS_EXECUTION_ENV=AWS_ECS_FARGATE
AWS_REGION=ap-northeast-1
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

cdkのdoctorで見ると↓みたいなのが出てきます

cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation doctor
:information_source: CDK Version: 1.110.1 (build 0028d09)
:information_source: AWS environment variables:
  - AWS_EXECUTION_ENV = AWS_ECS_FARGATE
  - AWS_CONTAINER_CREDENTIALS_RELATIVE_URI = /v2/credentials/xxxxxxxxxxxxxxxxxxxxxx
  - AWS_DEFAULT_REGION = ap-northeast-1
  - AWS_REGION = ap-northeast-1
  - AWS_STS_REGIONAL_ENDPOINTS = regional
  - AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  - AWS_SDK_LOAD_CONFIG = 1
:information_source: No CDK environment variables
END

勘の良い人であればなんか怪しいやつらがいることに気がつきますね?
こいつらです

- AWS_DEFAULT_REGION = ap-northeast-1
- AWS_REGION = ap-northeast-1

もしこいつらが原因ならリージョンとアカウントをCDKの bin/stack.tsに環境変数の値で直接渡せば解決するんじゃね?と考えました

↓は公式のサンプルでとりあえずこの通りに渡すことにしました
Environments - AWS Cloud Development Kit (CDK)

new MyDevStack(app, 'dev', { 
  env: { 
    account: process.env.CDK_DEFAULT_ACCOUNT, 
    region: process.env.CDK_DEFAULT_REGION 
}});

そして一番上の方で設定していた環境変数とは別に↓のような感じでCDK向けに新たに環境変数を追加しました

CDK_DEFAULT_ACCOUNT="${ACCOUNT_ID}"
CDK_DEFAULT_REGION="${ACCOUNT_REGION}"

そしてサンプルのように渡した上でdoctorでも確認をし問題なく設定されていることを確認しました

cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation doctor
...
:information_source: CDK environment variables:
  - CDK_DEFAULT_ACCOUNT = xxxxxxxxxx
  - CDK_DEFAULT_REGION = us-west-2

この状態でbootstrapを叩くと...

cdk --profile xxx --role-arn arn:aws:iam::xxxxxxxxxx:role/DeployRoleForCloudFormation bootstrap
CDK_DEFAULT_ACCOUNT:xxxxxxxxxx //これはconsole.logを仕込んだ
CDK_DEFAULT_REGION:ap-northeast-1 // これはconsole.logを仕込んだ

CDK_DEFAULT_REGION:ap-northeast-1😇

なんか勝手に上書きされました
先程の勘が良い人であれば、なんか上書きしそうなやつがいたことに心当たりがありますね?
こいつらです

- AWS_DEFAULT_REGION = ap-northeast-1
- AWS_REGION = ap-northeast-1

ここまでくるとCDKの内部の挙動について読む必要が出てくるので読んでいきます
(下記の推測が間違っている可能性もなくはないので、最終的には最新のコードを読んでください)

CDK_DEFAULT_REGIONここで定義されています

/**
 * Environment variable set by the CDK CLI with the default AWS region.
 */
export const DEFAULT_REGION_ENV = 'CDK_DEFAULT_REGION';

この変数で値を設定しているところを探します

async function populateDefaultEnvironmentIfNeeded(aws: SdkProvider, env: { [key: string]: string | undefined}) {
  env[cxapi.DEFAULT_REGION_ENV] = aws.defaultRegion;
  ...
}

aws.defaultRegionを探しますが、そもそもawsは引数で渡されているのでそちらから探します
関数自体の呼び出しはexecProgramの中で呼ばれています

export async function execProgram(aws: SdkProvider, config: Configuration): Promise<cxapi.CloudAssembly> {
  const env: { [key: string]: string } = { };

  const context = config.context.all;
  await populateDefaultEnvironmentIfNeeded(aws, env);
  ...
}

execProgramはCloudExecutableの内部で呼ばれていることが以下の二箇所からわかります

ここ

async function initCommandLine() {
  ...
  const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({
    profile: configuration.settings.get(['profile']),
    ec2creds: argv.ec2creds,
    httpOptions: {
      proxyAddress: argv.proxy,
      caBundlePath: argv['ca-bundle-path'],
    },
  });

  const cloudFormation = new CloudFormationDeployments({ sdkProvider });

  const cloudExecutable = new CloudExecutable({
    configuration,
    sdkProvider,
    synthesizer: execProgram,
  });
  ...
}

ここ

export class CloudExecutable {
  ...
  private async doSynthesize(): Promise<CloudAssembly> {
    ...
    while (true) {
      const assembly = await this.props.synthesizer(this.props.sdkProvider, this.props.configuration);
      ...
    }
    ...
  }
  ...
}

CloudExecutableのsdkProviderはinitCommandLine側で作られたものであることも同時にわかるので
SdkProvider.withAwsCliCompatibleDefaultsを探します

export class SdkProvider {
  ...
  public static async withAwsCliCompatibleDefaults(options: SdkProviderOptions = {}) {
    ...
    const region = await AwsCliCompatible.region({
      profile: options.profile,
      ec2instance: options.ec2creds,
    });

    return new SdkProvider(chain, region, sdkOptions);
  }
  ...

  public constructor(
    private readonly defaultChain: AWS.CredentialProviderChain,
    /**
     * Default region
     */
    public readonly defaultRegion: string,
    private readonly sdkOptions: ConfigurationOptions = {}) {
  }

   ...
}

defaultRegionはAwsCliCompatible.regionで取得した値を使用していることがわかったので更に探します

export class AwsCliCompatible {
  ...
  public static async region(options: RegionOptions = {}): Promise<string> {
    ...
    let region = process.env.AWS_REGION || process.env.AMAZON_REGION ||
      process.env.AWS_DEFAULT_REGION || process.env.AMAZON_DEFAULT_REGION;

    while (!region && toCheck.length > 0) {
      const opts = toCheck.shift()!;
      if (await fs.pathExists(opts.filename)) {
        const configFile = new SharedIniFile(opts);
        const section = await configFile.getProfile(opts.profile);
        region = section?.region;
      }
    }

    ...
    return region;
  }
}

ようやく終わりが見えました
ここまで来ると設定されるリージョンの優先順位は

process.env.AWS_REGION >
process.env.AMAZON_REGION >
process.env.AWS_DEFAULT_REGION >
process.env.AMAZON_DEFAULT_REGION >
プロファイルのセクションにあるリージョン >
その他の設定(ここはコードを見てください)

であることがわかりました
ここで元々の設定を振り返ると明らかに2つの環境変数が悪さをしていることがわかります

- AWS_DEFAULT_REGION = ap-northeast-1
- AWS_REGION = ap-northeast-1

しかしながらこれらの環境変数をunsetして実行するのが良いとは言いづらいので、
(Fargate側で何かに利用している可能性も否めない)
最終的に下記2つの環境変数を利用することをやめて、
元々自分で設定していた環境変数をbin/stack.tsで利用することで上書きを回避し問題なく動かす事ができました

CDK_DEFAULT_ACCOUNT
CDK_DEFAULT_REGION

AWS CDKでdiffが出るのにdeployでnochangesになる

最近CDK使ってて出会した
端的に言えばCFnが悪いやつ(下はGithubのIssue)

github.com

ただまあ公式にも書いてあるこれをどうにかしてデプロイしたい

CreationPolicy、DeletionPolicy または UpdatePolicy 属性単独では更新できません。
更新できるのは、リソースを追加、変更、または削除する変更を含める場合だけです。
たとえば、リソースのメタデータ属性を追加または変更することはできます。

docs.aws.amazon.com

しかしながら一部変更を適用するのに、
いちいちリソースを変更まではしたくないなあと思って適当に回避策を思いついて試したらうまく動いたので残しとく

まずは適当にCDKのアプリケーションを作る(今回はTypeScriptだけど他の言語でも出来るはず)

cdk init app --language typescript
# 作ると勝手にディレクトリ移動されるので今回はS3を使う
npm install @aws-cdk/aws-s3

作ったらまずは適当にBucketを作る

import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';

export class CdkTestStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    new s3.Bucket(this, 'TestBucket', {
      bucketName: 'ex-test-bucket-000000'
    });
  }
}

デプロイする

npm run cdk -- deploy

で、作ったあとにやっぱRemovalPolicyにDESTROYをつけたくなったとする

new s3.Bucket(this, 'TestBucket', {
  bucketName: 'ex-test-bucket-000000',
  removalPolicy: cdk.RemovalPolicy.DESTROY,
});

diffが出る(元々デフォルトでRetainがついてるので)

npm run cdk -- diff

> cdk-test@0.1.0 cdk
> cdk "diff"

Stack CdkTestStack
Resources
[~] AWS::S3::Bucket TestBucket TestBucket560B80BC 
 ├─ [~] DeletionPolicy
 │   ├─ [-] Retain
 │   └─ [+] Delete
 └─ [~] UpdateReplacePolicy
     ├─ [-] Retain
     └─ [+] Delete

デプロイするとNo changes!!!!😇

npm run cdk -- deploy

> cdk-test@0.1.0 cdk
> cdk "deploy"

CdkTestStack: deploying...
CdkTestStack: creating CloudFormation changeset...

 ✅  CdkTestStack (no changes)

というわけで↓を新たに入れておく

new cdk.CfnOutput(this, 'UpdateTime', {
  description: 'UpdateTime',
  value: new Date().toLocaleString(),
})

diffが増える

npm run cdk -- diff

> cdk-test@0.1.0 cdk
> cdk "diff"

Stack CdkTestStack
Resources
[~] AWS::S3::Bucket TestBucket TestBucket560B80BC 
 ├─ [~] DeletionPolicy
 │   ├─ [-] Retain
 │   └─ [+] Delete
 └─ [~] UpdateReplacePolicy
     ├─ [-] Retain
     └─ [+] Delete

Outputs
[+] Output UpdateTime UpdateTime: {"Description":"UpdateTime","Value":"2021/7/22 16:51:32"}

デプロイをする
今回はリソースを追加したので当たり前だが成功する

npm run cdk -- deploy

> cdk-test@0.1.0 cdk
> cdk "deploy"

CdkTestStack: deploying...
CdkTestStack: creating CloudFormation changeset...

 ✅  CdkTestStack

Outputs:
CdkTestStack.UpdateTime = 2021/7/22 16:52:02

今度はRemovalPolicyをRETAINに戻したくなったとする

new s3.Bucket(this, 'TestBucket', {
  bucketName: 'ex-test-bucket-000000',
  // removalPolicy: cdk.RemovalPolicy.DESTROY, これを削除する
});

diffを見る
(ぱっと見、上で失敗したときのパターンに見える)

npm run cdk -- diff

> cdk-test@0.1.0 cdk
> cdk "diff"

Stack CdkTestStack
Resources
[~] AWS::S3::Bucket TestBucket TestBucket560B80BC 
 ├─ [~] DeletionPolicy
 │   ├─ [-] Delete
 │   └─ [+] Retain
 └─ [~] UpdateReplacePolicy
     ├─ [-] Delete
     └─ [+] Retain

これをdeployするとOutputsのUpdateTimeが変わる為、CDK(CFn)はちゃんとデプロイしてくれる

npm run cdk -- deploy

> cdk-test@0.1.0 cdk
> cdk "deploy"

CdkTestStack: deploying...
CdkTestStack: creating CloudFormation changeset...


 ✅  CdkTestStack

Outputs:
CdkTestStack.UpdateTime = 2021/7/22 16:56:21

再度diffを叩くと何も変更なしになる
(リソースへの変更は適用された為)

npm run cdk -- diff

> cdk-test@0.1.0 cdk
> cdk "diff"

Stack CdkTestStack
There were no differences

この状態でdeployするとUpdateTimeに対しての変更がかかる

npm run cdk -- deploy

> cdk-test@0.1.0 cdk
> cdk "deploy"

CdkTestStack: deploying...
CdkTestStack: creating CloudFormation changeset...


 ✅  CdkTestStack

Outputs:
CdkTestStack.UpdateTime = 2021/7/22 16:58:23

とまあこんな感じでOutputsにUpdateTimeとかを設定しておけば、
管理したいリソースに対して変更を加えずともOutputsに変更を加えて半強制的にデプロイをすることが出来て便利という小技

GC本を読んで学ぶ2

  • 全てのガベージコレクションは以下のいずれかに基づいている(ガベージコレクタ毎にさまざまな方法で領域ごとにこれらのアプローチを組み合わせているらしい)
    • マークスイープGC
    • コピーGC
    • マークコンパクトGC
    • 参照カウントGC

大体wikiに載ってる

  • ストップザ・ワールド的なアプローチ例
    • コレクタスレッド1
      • ガベージコレクションを実行するスレッド
    • ミューテータスレッドN
      • アプリケーションコードを実行するスレッド(コレクタスレッド実行時は停止)

これの利点はヒープのスナップショットがとれる + ヒープ中のオブジェクトのトポロジが変化しないこと

  • 自動メモリ管理を行うのに必要な機能
    • 新しいオブジェクトの領域の割当
    • 生きているオブジェクトの特定
    • 死んだオブジェクトの使用している領域の回収

完全に判別するのは厳しいので参照を追って参照が存在しないオブジェクトは死んでいる判定にすることによって安全に回収する

  • マークスイープGC
    • まずルート集合(レジスタ、スレッドのスタック、グローバル変数)からオブジェクトのグラフを走査して到達可能なオブジェクトにマークをつける(所謂tracing)
    • スイープフェーズでヒープ中の全てのオブジェクトにマークがついているか調べてなければゴミとして回収する(わかりやすい)
    • 間接的GCアルゴリズム
    • ゴミを探すというよりは生きているものを探して他は全てゴミとして扱う

Mark-and-Sweep: Garbage Collection Algorithm)

わかりやすい
コレクタがシングルスレッドなら探索リストをDFSで実装できる(これによってハードウェアキャッシュがあたることを期待できる)

三色抽象化を利用してオブジェクトの状態を表現できるらしい
黒(処理済み)、白(未処理(永遠に未処理の可能性もある))、灰(処理途中もしくは再処理が必要)
並行GCのアルゴリズムで特に有用らしい

  • 時間的局所性

    python total = 0 for i in range(1, 11): total += i

    とした場合にtotalに対して複数回のアクセスが発生するので、その値をキャッシュするのが有効(アドレスをイメージしたほうがわかりやすそう)

  • 空間的局所性

    python l = [1,2,3,4,5] for i in l: print(i)

    この場合lに対して複数回のアクセスが発生するので、その値付近をキャッシュするのが有効(これもメモリが連続して確保されている際にアクセスすることを考えたほうがわかりやすいか、CPUのプリフェッチとかもこれ系)

マークビットの領域は通常オブジェクトのヘッダワードの中に用意する
いまいちオブジェクトのヘッダワードのイメージがよくわからんとなったので適当に調べた感じ
Javaだとこんな感じらしい

セットアソシアティブ方式とダイレクトマッピング方式はこれがめちゃくちゃわかりやすかった
(アドレス空間/キャッシュ容量/ブロックサイズ/タグ/オフセット/インデックスとか用語から説明されてて助かる(基本と応用とったくせに完全に記憶から消失した))

オブジェクトヘッダにマークビットを用意することによってrace conditionを解消できる(外部に持つと更新状態が競合する可能性が存在する) ビットマップではなくバイトマップを使うとマーク処理のrace conditionが解消できるらしい(よくわかってない)

  • 安全性に関しての結論
    • コレクタはどこに格納された値であってもミューテータが保持する値を書き換えてはいけない(オブジェクトを移動させるどんなアルゴリズムも使えない, 移動するとポインタの修正が発生する)
      • オブジェクトのようなもの(のようなものって具体的になんなんだろう)のマークビットを変更することによってユーザのデータを破壊する恐れがある
    • ミューテータがコレクタのデータと干渉する機会を最小限にすることが有益
      • ヘッダワードに持つよりはオブジェクトと分離したデータ構造(これがバイトマップかな)に保存したほうがリスクが小さい
      • コレクタによるページング回数を減らすのも有益

マークスイープGCのメモリアロケータはフラグメンテーションを避ける為に一定サイズのブロックを連続して配置するらしい
(メモリアクセスのパターンが均一になってハードウェアのプリフェッチの機構と相性が良い)
マークフェーズのオブジェクトヘッダの読み取りによって発生するキャッシュミスの回数を減らす方法の一つにBiBoPというものがあるらしい
(同じページには単一の型のみを割り当てることによってオブジェクト毎の型情報をページ単位に圧縮が出来る)

GC本を読んで学ぶ1

ガベージコレクション: 自動的メモリ管理を構成する理論と実装を積ん読していたので消化するために前に読んでたメモをとりあえず残す

第一章

  • 動的なメモリ割り当てを利用することによって、コンパイル時に総サイズがわからないオブジェクトでも割り付けたり、開放したりすることができる
  • 動的に割り付けたオブジェクトはヒープに保存される
  • アクティベーションレコードとはなんぞ?(スタック内の割付らしい)
  • スタックフレームという領域があるらしい(よくわからん)
  • 静的割付(オブジェクトの名前をコンパイル時あるいはリンク時に決定するメモリ上の場所に束縛する方法)
  • なぜヒープへの割付が重要か?
    • オブジェクトのサイズを動的に決められる(配列の上限値をハードコードするとプログラムが壊れる恐れがある)
    • 再帰的なデータ構造を定義できる(リスト、ツリー、マップなど)
    • 新しく生成したオブジェクトを親の手続きに返せる(これによってファクトリーメソッドなどの技法を使える)
    • 関数の値として別の関数を返せる(所謂クロージャやサスペンション←初めて聞いた)
  • ヒープに割り付けたオブジェクトには参照を用いてアクセスする(ポインタ、ハンドル)
  • ハンドルを利用することで、オブジェクトの再配置が容易になる
  • 明示的解放(CでいうfreeやC++のdeleteなど)
  • 手動で解放する場合はプログラミングエラーを犯すリスクが有る(エラーは2種類)
    • 一つはメモリを解放するのが早すぎるエラーで、メモリへの参照が残っているのに誤って解放してしまうケース(このような参照を宙ぶらりんポインタと呼ぶ)
      • 例: A ref B ref C
      • Bを手動解放した場合にAはBへの参照が残り、Cは到達不能となり解放することもできなくなる
      • 宙ぶらりんのポインタを検出する方法の一つにファットポインタがある
        • ファットポインタには、ポインタが指しているオブジェクトとポインタ自身のバージョン番号を格納しておく
      • 参照をたどるような操作の際には、必ずポインタに格納してあるバージョン番号が、オブジェクトのバージョンと一致していることをチェックする
        • (この手法は、オーバーヘッドのため、デバッキングツールでしか使えないことが多い, また完全に信頼できる方法でもない)
    • 二つめはメモリリーク
      • 先程の例のように一箇所のメモリ解放の誤りが宙ぶらりんポインタ(A)とメモリリーク(C)の両方を引き起こしてしまうこともありうる
  • スレッドセーフなデータ構造にアクセスするアルゴリズムでは、さまざまな問題に対処する必要がある(デッドロック、ライブロック、ABAエラー)
  • ガベージコレクションは宙ぶらりんポインタが作られるのを防止する(オブジェクトが回収されるのは、到達可能なオブジェクトからのポインタが1つもなくなった時だけ)
  • GCは原則的にすべてのごみを解放することを保証する(すなわち到達不能なオブジェクトは全て最終的にはGCによって回収される)
  • 注意点は以下
    • 追跡型GC(Tracing collection)が用いる「ごみ」の定義は、決定可能なものであり、二度とアクセスされないオブジェクトすべてがごみとみなされるわけではない
    • GCの実装によっては、効率上の理由からオブジェクトの一部を回収しないことがあるということ
  • ガベージコレクタのみがオブジェクトを解放するので、二重解放問題は生じない(解放に関するすべての決定はコレクタが行う)
  • コレクタはヒープ内のオブジェクトの構造やオブジェクトにアクセスする可能性のあるスレッドに関するグローバルな情報を持っている
  • ガベージコレクションが望ましい理由は、コードを書くのが簡単になるということではない

    • 簡単にはなるが、それよりも重要なのはガベージコレクションによってメモリ管理の問題がインターフェイスから切り離せるということ
    • ガベージコレクションなしではメモリ管理の問題がプログラム中に散乱することになる
    • ガベージコレクションは再利用性を改善する、これが様々な分野でほぼすべての近代的な言語で必須とされている理由
    • ガベージコレクションはスペースリークが絶対にないという保証はできない
    • なぜなら、データ構造が到達可能なまま際限なく大きくなる場合(キャッシュ等)ガベージコレクションでは解決できない
    • さらに到達可能だが二度とアクセスされないような場合についても同様
  • GCに求められる要素

    • 安全性
      • 生きているオブジェクトの回収などを行わないこと
      • スループット
      • mark/cons比
    • 完全性
      • ヒープ中のごみがすべて回収されること, かなり難しい(常にそれが最適とは限らない)
      • 並行GC中に新たにでたごみを浮遊ごみと呼ぶ(そのため、並行GCの文脈では完全性をすべてのごみがいずれは回収されることとしたほうが良いことになる)
    • 即時性
      • GCアルゴリズムによって異なる、ここでもまた時間と空間のトレードオフが導かれる
    • 停止時間

AWS Batchのexporterを書いてみた

久しぶりのブログ更新
ここ最近読書によるインプット過多で全然アウトプットしていないので久しぶりにまともなものを書く

golangの勉強がてら書いてみたので見る人が見たら酷いかもしれない
シンプルにそれぞれのStateのJobをメトリクスとして取得してみた
Queue毎にカウントしただけのメトリクスを吐き出すことも考えたけど、PromQLで計算すれば出来るから一旦はこのままにしてみた
github.com

コード書いたついでに久しぶりにGrafanaも周りも触ったらかなり改善されていい感じだったので、仕事でもGrafana使いたい欲がすごい(今はDatadogを主に使ってる)