りゅうじの学習blog

学習したことをアウトプットしていきます。

DNSの仕組み

DNSはフルリゾルバとネームサーバーの2つで構成されている

  • DNSを通じてドメイン名からIPアドレスを引き出すのを「名前解決」という
  • ドメインの階層ごとにネームサーバーが設置されていて管理する階層のことを「ゾーン」という
  • フルリゾルバがしばらくキャッシュするので毎回聞きに行くわけではない
  • DNSドメイン名とIPアドレスの紐付け以外にも管理している

7/20 ユーザのアイコンの色をランダムに割り当てる

要件

  • アイコンの文字は名前の1文字目を表示する
  • 色は5色からランダムで設定する
  • 色はユーザーに固有のもので、再読み込みなどで変更されない。

やり方

  • 登録日時を文字コードにし、ランダムのhsl( 色相(Hue)、彩度(Saturation)、輝度(Lightness))で色を割り当てる、濃淡は調整可能なのでデザイナーと擦り合わせる(今回は hを登録日時の文字コード sが30 lが60)

参考

Compute an arbitrary color for user avatar starting from their username (with Javascript).

// Import
import React, { VFC, useMemo } from 'react';
import styled from 'styled-components';
import { managementColors } from '../../../../themes/colors';

// Types
type ContainerProps = { item: MemberListItem };
type Props = { item: MemberListItem; icon: string; iconColor: string };
export interface MemberListItem {
  id: string;
  icon?: string;
  name: string;
  email: string;
  createdAt: string;
}
interface IconProps {
  color: string;
}

// Style

// 省略

const Icon = styled('div')<IconProps>`
  width: 32px;
  height: 32px;
  border-radius: 50%;
  margin-right: 12px;
  background-color: ${(props) => props.color};
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  font-weight: 700;
`;

// DOM
const Component: VFC<Props> = ({ item, icon, iconColor }) => {
  return (
       <>
        <Icon color={iconColor}>{icon}</Icon>
        <div>
          {item.name}
          <br />
          <Email>{item.email}</Email>
        </div>
       </>
  );
};

// Container
const Container: VFC<ContainerProps> = ({ item }) => {
  const dateToHslColor = (dateStr: string, s: number, l: number): string => {
    let hash = 0;
    Array.from(dateStr).forEach((char) => {
      // eslint-disable-next-line no-bitwise
      hash = char.charCodeAt(0) + ((hash << 5) - hash);
    });

    const h = hash % 360;
    return `hsl(${h}, ${s}%, ${l}%)`;
  };
  const getInitial = (name: string) => {
    return name[0];
  };
  const getRandomColor = useMemo(() => {
    return dateToHslColor(item.createdAt, 30, 60);
  }, [item.createdAt]);
  const icon = getInitial(item.name);
  const iconColor = getRandomColor;

  return <Component {...{ item, icon, iconColor }} />;
};

export { Container as MemberListCell };
  1. dateToHslColor(dateStr: string, s: number, l: number): string

    この関数は、渡された日付文字列をベースに一意の色を生成します。その色はHSL(Hue, Saturation, Lightness)形式で表されます。

    具体的には、日付文字列からハッシュ値を計算し、そのハッシュ値を360で割った余りを色相(Hue)として使用しています。なぜ360で割るのかというと、色相は通常0から360の範囲で表されるからです。関数の2つ目と3つ目の引数は彩度(Saturation)と輝度(Lightness)を指定するために使われます。

  2. getInitial(name: string)

    この関数は、名前の最初の文字(イニシャル)を返します。ここでは、アイコン上に表示する文字として使用されます。

  3. getRandomColor

    この関数は、useMemoフックを使って生成された色の値をメモ化(キャッシュ)します。ここでのキーはitem.createdAtで、この値が変わると新しい色が生成されます。

これらの関数を組み合わせることで、各ユーザーが作成された日時に基づいて異なる色のアイコンを持つようになります。この方法はユーザーが視覚的に区別しやすくなり、特定のユーザーを一目で認識できるようになります。

コメント

元々、私が考えた実装では、ユーザ名を文字コードにして合計値を指定された色の数で割り、その余りから色を選択するロジックにしていました。

ですが、実装後に、これでは同姓同名では一緒の色になってしまうのと、当初の色は5色だったんですが、5色では被る人が多く出てきてしまうので、登録日時にし、色も5色ではなく、hslのslのみを固定して、hをcreateAtの文字コードによって変化するようにすることで、対応しました。

7/15 TypeScript デコレータ

デコレータ

メタプログラミングに役にたつ

他の開発者が使いやすい道具を提供することに向いている

使うための準備

tsconfig.jsonの設定

"target": "es6"
"experimentalDecorators": true
  • 上記の二つを設定する必要がある

クラスデコレータ

function Logger(constructor: Function) {
  console.log("ログ出力中…");
  console.log(constructor);
}

@Logger
class Person {
  name = "Max";

  constructor() {
    console.log("Personオブジェクトを作成中…");
  }
}

const pers = new Person();

console.log(pers);
  • Loggerというデコレータを定義している
    • 大文字から始めなければいけないわけではないが、大文字から定義されていることが多い
      • Personクラスの前に@Loggerとしてデコレータを定義

重要なこととして、デコレータが呼び出されるのはインスタンス化された時ではなく、@Loggerのタイミングで呼び出されるということ

出力結果

ログ出力中…
app.js:10 class Person {
    constructor() {
        this.name = "Max";
        console.log("Personオブジェクトを作成中…");
    }
}
app.js:15 Personオブジェクトを作成中…
app.js:22 Person {name: 'Max'}
  • ログ出力中とデコレータで呼び出されているコンストラクタ関数が、Personがインスタンス化された際に呼び出される出力(Personオブジェクトを作成中、Person {name: 'Max'})より前に呼び出されている

デコレータファクトリ

デコレータ関数を何かに割り当てるときにデコレータをカスタマイズできるようにするもの

function Logger() {
  return function (constructor: Function) {
    console.log("ログ出力中…");
    console.log(constructor);
  };
}

@Logger()
class Person {
  name = "Max";

  constructor() {
    console.log("Personオブジェクトを作成中…");
  }
}

const pers = new Person();

console.log(pers);
  • Loggerのデコレータの中で無名関数を定義する
    • これで新しい関数を返す関数を定義することができた
  • これをデコレータとして適応するために、Logger()と関数として実行する必要がある

使い方の例

function Logger(logString: string) {
  return function (constructor: Function) {
    console.log(logString);
    console.log(constructor);
  };
}

@Logger("ログ出力中 - PERSON")
class Person {
  name = "Max";

  constructor() {
    console.log("Personオブジェクトを作成中…");
  }
}

const pers = new Person();

console.log(pers);
  • ファクトリ関数を実行したときに、その中の無名関数内で使う値をカスタマイズできるようになった
    • @Logger("ログ出力中 - PERSON")では関数を実行しているのではなく、デコレータ関数を返す関数を実行している

複数のデコレータを実行順序

function Logger(logString: string) {
  console.log("LOGGER ファクトリ");
  return function (constructor: Function) {
    console.log(logString);
    console.log(constructor);
  };
}

function Template(template: string) {
  console.log("TEMPLATE ファクトリ");
  return function (constructor: Function) {
    console.log(template);
  };
}

@Logger("ログ出力中 - PERSON")
@Template("テンプレートを出力中")
class Person {
  name = "Max";

  constructor() {
    console.log("Personオブジェクトを作成中…");
  }
}

const pers = new Person();

console.log(pers);
  • Templateファクトリ関数を追加して、@Templateデコレータを@Loggerデコレータの後に実行した
  • この時の実行順序を確認します
LOGGER ファクトリ
app.js:16 TEMPLATE ファクトリ
app.js:18 テンプレートを出力中
app.js:11 ログ出力中 - PERSON
  • ファクトリ関数としては、基本通りに上から順番に実行されている
  • 注目すべきは、ファクトリ関数の内部の無名関数の実行順序は、テンプレートのほうが先に実行されていることです

デコレータが複数定義されていたら、デコレータの内部の実行順序としては、したから順番に出力されます。

コメント

NestJSでデコレータをなんとなく触っていて、よくわかっていなかったので、少しずつ紐解いていけてる感じです。

7/14 PostgreSQLが接続できなくなった対処

どのタイミングで発生したか

  • 普段dockerで開発しており、dockerを使用せずに、npm run devで立ち上げて動作確認したいものがあったため、久しぶりにnpm run dev をしたところ、PostgreSQLに接続できないエラーで立ち上げられなかった

なぜ発生したか

  • PostgreSQLは「共有メモリ」を使用する
    • 共有メモリは複数のプロセス(ここではPostgreSQLの異なる部分や、別のソフトウェア)が同時にアクセスできるメモリ領域です
      • PostgreSQLは、データベースの操作を効率的に行うため、この共有メモリを必要とします。
        • 共有メモリの総量は限られており、その上限はオペレーティングシステムによって設定されます
          • その設定値を「SHMALL」パラメータと言います
            • 今回のエラーは、PostgreSQLが必要とする共有メモリの量が、オペレーティングシステムが設定しているSHMALLパラメータの上限を超えてしまったことを示しています
            • PostgreSQLが必要とするメモリがOSに割り当てられていないため、データベースの初期化ができていないということ

用語の整理

  1. カーネルパラメータ: カーネルとは、コンピュータのオペレーティングシステムの中心部分で、ハードウェアとソフトウェアの間で仲介役を果たします。カーネルパラメータは、このカーネルの動作を制御するための設定値のことを指します。
  2. 共有メモリ: コンピュータには、複数のプロセス(基本的には個々のプログラムの実行単位)があります。これらのプロセスがデータを共有するために使うメモリ領域のことを共有メモリと呼びます。共有メモリを使うと、プロセス間で大量のデータを効率的にやりとりできます。
  3. SHMALL: これは、システム全体で使用可能な共有メモリの最大値を制御するカーネルパラメータです。SHMALLの値は、通常はシステムの物理メモリの一部として設定されます。この値を増やすと、プロセスが共有メモリをより多く使えるようになります。しかし、その分、他のプロセスが使えるメモリは減少するため、この値を適切に設定することが重要です。

使用コマンド

現在のSHMALL値を確認

sysctl -a | grep shm

私の設定されていた値

kern.sysv.shmmax: 4194304
kern.sysv.shmmin: 1
kern.sysv.shmmni: 32
kern.sysv.shmseg: 8
kern.sysv.shmall: 1024
security.mac.posixshm_enforce: 1
security.mac.sysvshm_enforce: 1

このファイルはなければ新規作成する必要があります

sudo nano /etc/sysctl.conf

以下2行を設定します

kern.sysv.shmmax=設定したい値
kern.sysv.shmall=設定したい値
  • 値は自分の設定したい値を入力

編集が完了したら Ctrl+X を押し、 Y を押してファイルを保存し、最後に Enter を押して終了します

今回のセッション中のみ、こちらのコマンドで上記の設定を反映させられます

sudo sysctl -w kern.sysv.shmmax=1073741824
sudo sysctl -w kern.sysv.shmall=2097152

こちらのコマンドで反映されているかの確認

sysctl -a | grep kern.sysv.shm

再起動してもこの設定が反映されるようにする

なぜ反映されないか

セキュリティ上の制約から、macOS/etc/sysctl.conf の値を起動時に読み込むことが許されていないから

解決法

macOSのlaunchdを使用して、起動時にsysctlコマンドを実行する.plistファイルを作成する

手順

/Library/LaunchDaemons ディレクトリに sysctl.plist ファイルを作成

sudo nano /Library/LaunchDaemons/sysctl.plist

sysctl.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>local.sysctl</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/sbin/sysctl</string>
        <string>-w</string>
        <string>kern.sysv.shmmax=1073741824</string>
        <string>kern.sysv.shmall=2097152</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
</dict>
</plist>

このXMLファイルは、macOSの起動時に特定のコマンドを実行するための設定を持つ「プロパティリスト」(plist)ファイルです。具体的には次のような動作を指定しています

  • Labelキー: このジョブの一意の識別子を指定します。ここではlocal.sysctlとなっています。
  • ProgramArgumentsキー: 実行するコマンドとその引数を指定します。ここでは、/usr/sbin/sysctlコマンドを、w kern.sysv.shmmax=1073741824およびkern.sysv.shmall=2097152という引数で実行するように指定しています。これにより、システムの共有メモリに関する設定を変更しています。
  • RunAtLoadキー: このジョブをシステム起動時に実行するかどうかを指定します。trueと指定されているので、システム起動時に上述のコマンドが実行されます。
  • KeepAliveキー: ジョブが何らかの原因で終了した場合に自動的に再起動するかどうかを指定します。ここではfalseと指定されているので、一度実行されたジョブは再起動されません。

このplistファイルを/Library/LaunchDaemons/ディレクトリに保存し、適切な権限を設定することで、システム起動時にこれらの設定が自動的に適用されるようになります。

作成したファイルのパーミッションを設定

sudo chown root /Library/LaunchDaemons/sysctl.plist
sudo chmod 644 /Library/LaunchDaemons/sysctl.plist
  1. sudo chown root /Library/LaunchDaemons/sysctl.plist:このコマンドは/Library/LaunchDaemons/sysctl.plistファイルの所有者をrootに変更します。chownUNIX系OSでファイルの所有者やグループを変更するためのコマンドです。rootは最高権限を持つユーザーで、このファイルを安全に管理するためにはrootユーザーの所有とすることが一般的です。
  2. sudo chmod 644 /Library/LaunchDaemons/sysctl.plist:このコマンドは/Library/LaunchDaemons/sysctl.plistファイルのパーミッション(許可設定)を変更します。具体的には、所有者(この場合はroot)に対して読み書きのパーミッションを、それ以外のユーザーに対しては読み取りのみのパーミッションを与えます。chmodUNIX系OSでファイルのパーミッションを変更するためのコマンドで、644は一般的なパーミッション設定の一つです。

plistをロード

sudo launchctl load -w /Library/LaunchDaemons/sysctl.plist
  • このコマンドはlaunchctlツールを使用して、/Library/LaunchDaemons/sysctl.plistというLaunchDaemonをロード(起動)します。-wオプションは、plistファイルの設定に関わらず、このDaemonを永続的に有効化することを指示します。これにより、このDaemonはシステム起動時に自動的にロードされるようになります。

再起動して設定が反映されているか確認

 sysctl -a | grep kern.sysv.shm

解決しました

7/10 AzureFunctionsが複数exportされた関数を特定するためのentryPointを明示的に指定する

なぜやるか

Azure Functionsでは、JavaScriptやTypeScriptで書かれた関数をエクスポートするとき、その関数が実行されるべきものであることを明示的に示す必要があります。これは、Azure Functions ホストが関数アプリ内のファイルからどの関数を実行するかを判断するための情報です。 もしエクスポートされた関数が一つだけであれば、Azure Functions ホストはその関数をデフォルトで実行します。しかし、複数の関数がエクスポートされている場合、ホストはどの関数を実行すべきか判断できません。そのため、特定の関数を実行するように指示するために、entryPoint プロパティを使用して関数名を指定します。

  • AzureFunctionsのentryPointを指定していないことで、どの関数を実行すればいいのか、特定できずに実行されなかった
  • これまでは、同時刻の定期実行タスクがなかったので、問題なかった
  • 複数の関数を同時刻に定期実行する場合は、entryPointを明示的に指定する必要がある

Testディレクトリにfunction.jsonとindex.tsがあると仮定します

function.json

{
  "bindings": [
    {
      "name": "Test",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 30 8 * * *"
    }
  ],
  "scriptFile": "../built/functions/Test/index.js",
  "entryPoint": "default"
}
  • entryPointを指定

index.ts

// eslint-disable-next-line import/no-unresolved
import { AzureFunction } from '@azure/functions';

const fn: AzureFunction = async function 関数名(
  context
) {
  try {
    await 定期実行したい関数()
    );
  } catch (err) {
    context.log.error(err);
  }
};

export default fn;

azure-functionsディレクトリに、function.jsonとindex.tsがあります

function.json

{
  "bindings": [
    {
      "name": "__function__",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */1 * * * *" // fix this
    }
  ],
  "scriptFile": "../built/functions/__function__/index.js",
  "entryPoint": "default"
}
  • entryPointを指定

index.ts

import { AzureFunction } from "@azure/functions";

const fn: AzureFunction = async function __function__(context) {
  try {
    // run task here
  } catch (err) {
    context.log.error(err);
  }
};

// Do not change this line. Azure Functions need to one default export function.
export default fn;

7/6 GA 主要指標の定義と意味

Googleアナリティクスでは、多くの意表があるが、中でも、「ユーザー>概要」に表示される指標が重量指標である。

  • 重要指標は多くのレポートで使われる
  • 重要指標は設定によって多少変化する
    • ユーザー分析のオンオフで変化する
      • 切り替えはいつでもできるので適宜変更すれば良い

主要指標

ユーザー

ユニークなブラウザの訪問者数。

一定期間中の同一訪問者の訪問はマージされ、ダブルカウントされない。

セッション

前提として、訪問=セッション。

ユーザーのひと続きのページの閲覧。

ユーザーがページ閲覧開始した時点から訪問が開始する。

ユーザーの操作がないまま30分が経過したら、訪問は終了する。

30分経過しなくても以下が発生すると新しい訪問が開始する。

  • 午前0時の経過
  • 参照元の切り替わり
  • メディアの切り替わり
  • キャンペーンの切り替わり
    • 異なったキャンペーンパラメータが付与されたURLの表示
    • 自動タグで計測しているAdWords広告のクリック

新規ユーザー

サイトに初めて訪問したユーザーのセッションが「新規ユーザー」です。

ページビュー数

ページが表示された延べ回数です。

セッション中に同じページが表示された場合、ブラウザのリロード等で同じページが連続して閲覧された場合でも構わずカウントする。

ユーザーあたりのセッション

「セッション」を「ユーザー」で割ったものが「ユーザーあたりのセッション」です。

新規セッション率

全セッションにおける「新規ユーザー(=新規セッション)」の割合をパーセンテージであらわしたもの。

ページ/セッション

ページビュー数をセッションで割ったもの。

平均セッション時間

セッション毎のサイト滞在時間の平均。

セッション開始時点のタイムスタンプと、セッションの最終ヒットのタイムスタンプを差し引いて求める。

Aさん10分 + Bさん20分 / 2(セッション数) = 15分(平均セッション率)

直帰率

全セッションに対する「直帰セッション」の割合。

「直帰セッション」とは、1セッションで1ページビューしか閲覧されなかったセッションのこと。

コンバージョン率

「ユーザー>概要」にはないが、主要指標である。

全セッションにおける、コンバージョンが発生したセッションの割合で計算される。

eコマースのコンバージョン率

「ユーザー>概要」にはないが、主要指標である。

全セッションにおける、トランザクションが発生した回数の割合で計算される。