だからお前のデータはぶっ壊れる by キャッシュ

## Q. キャッシュすれば早くなるんでしょ？

A. ちゃんとやらないとデータが壊れる。

## 0. ソシャゲー作ったら表示が遅いwwwww

**「キャッシュすれば良いじゃん！」**

## 1. なんか、過去のデータに戻ったんですけどwwww

キャッシュを導入した途端に発生です。何が起きたのでしょうか。

### キャッシュロジック

取得時

```javascript
function getData(id) {
  data = getCache(id) // キャッシュを取得する
  if(data == null ) { // キャッシュが無い
    data = getStorage(id) // ストレージから取得する
    setCache(id, data) // キャッシュに設定する
  } 
  return data //返却する
}
```

更新時

```javascript
function saveData(id, data) {
  setStorage(id, data) // ストレージに設定する
  setCache(id, null) // キャッシュをクリア(無効化)する
}
```

よくあるロジックです。問題無いように見えます。

### 不整合が起きます!!

```javascript
function saveData(id, data) {
  setStorage(id, data)
  setCache(id, null)  // ← ここでエラー!!
}
```

1. 更新時に、**新しいデータ**がストレージに保存されました。
2. エラーによって、**キャッシュだけ古い**データになります。
3. 取得時は、キャッシュされてるので、古いデータが返却されます。

### さらに

1. 古いキャッシュを元に、ユーザーが**更に新しい**データを保存。
2. ストレージのデータは**無視**されて、書き込まれます。

「 さっき取ったアイテムが反映されないwwwww 」

## 2. なんか、過去のデータに戻ったんですけどwwww2

「古いキャッシュを残さないように、先にキャッシュを消そう。」

```javascript
function saveData(id, data) {
  setCache(id, null)
  setStorage(id, data) // ← ここでエラーでも怖くない!!
}
```
storageの更新でエラーになっても、キャッシュが消えるだけ。
やったね。完璧だね！！

### 不整合が起きます!!

```javascript
function saveData(id, data) {
  setCache(id, null)
  // 別クライアントからのアクセスで取得処理が走る。
  setStorage(id, data)
}
```
1. キャッシュがクリアされる
2. 取得処理が割り込み、更新前のdataがキャッシュに設定される。
3. storageだけ更新されて**新しくなる。**
4. 取得時は、**古い**キャッシュが返却される。
5. 以下略。

エラーも起きないので気づかないことになります。

## 3. もうどうしたらいいかわからない、、、

悲観ロックを使いましょう。

取得時

```javascript
function findData(id) {
  if( lock(id) ) {
    data = getCache(id)
    if(data == null ) {
      data = getStorage(id)
      setCache(id, data)
    } 
    lookoff(id)

    return data
  } else {
    //まつとかエラーとか
  }
}
```

更新時

```javascript
function saveData(id, data) {
  if(lock(id)) {
    setCache(id, null)
    setStorage(id, data)
    lockOff(id)
  }
}
```

lock中は、取得できないようなりました。
これなら割り込まれることもありません。
ただキャッシュしたいだけなのにコードが複雑になってきました。


### 不整合が起きます!!

```javascript
function saveData(id, data) {
  if(lock(id)) {
    setCache(id, null)
    setStorage(id, data)
    lockOff(id) // ←ここでエラー
  }
}
```

「ずっとー取得できないんだけど、、、」

## 4. もしもの備え

```javascript
function saveData(id, data) {
  if(lock(id, "1s" )){ // 放っておくと1s後に消える。
    setCache(id, null)
    setStorage(id, data)
    lockOff(id) // ここでエラーになっても、1s後には取得できる
  }
}
```

ロックのttlも忘れずにしましょう。

完璧ですね!!

## 5. キャッシュしたら前より遅いんだけどwwwww

取得時

```javascript
function findData(id) {
  if( lock(id) ) {
    data = getCache(id)
    if(data == null ) {
      data = getStorage(id)
      setCache(id, data)
    } 
    lockoff(id)
    return data
  } else {
    //まつとかエラーとか        ←    ！！！！！！！！！！！！！
  }
}
```

アクセス数が多いとロックさてしまうことが増えます。

```javascript
function findData(id) {
  data = getCache(id) // キャッシュがあればロックもみないで返す。
  if(data == null ) {
    if( lock(id) ) {
      data = getStorage(id)
      setCache(id, data)
      lockoff(id)
    } else {
      //まつとかエラーと
    } 
    return data
  } 
}
```
キャッシュがある場合は、ロックしないで**少し**マシ。

## 6. 更新が多いとやっぱりだめwww

更新時に、同じデータをキャッシュに設定しましょう。
これをライトスルーと呼んだりします。

```javascript
function saveData(id, data) {
  setCache(id, data)
  setStorage(id, data)
}
```

やったね!!

## 7. ブラウザ再起動したらデータが消えた。治らないwww

```javascript
function saveData(id, data) {
  setCache(id, data)
  setStorage(id, data) // ←ここでエラー!!
}
```

キャッシュにだけ最新データが作られて、ストレージには存在しなくなりました。

もう、ハチャメチャですね。

## 8. 色々考えてみよう！

この先は、あなたが探してみてください。

### ポイント

* 処理順序が大切。全ての箇所でエラーが起きます。
    * 起きた場合どんなことになるか考慮が必要。
* 割り込みはいつでもされます。
    * エラー以上に複雑なタイミングが増えます。
* 仕様を見直しましょう。
  * そもそも古いデータでも問題ないようにする

# まとめ

**「キャッシュすれば良いじゃん！」**
**「ほんとうに？」**