エイエイレトリック

なぐりがき

djangoのFileBasedCacheの使い方と仕組み

FileBasedCache の使い方と仕組みについてメモします。

データベースを用意するまでもないけど、情報をキャッシュしたいときによさそうです。

ファイルで出力するのでキャッシュしたかどうか、キャッシュが残っているかどうかがわかりやすいためデバッグしやすい気がします。

また、キャッシュの中身も比較的簡単に確認できます。

動作環境

使い方

設定

設定ファイル (settings.py, settings/*.py など) でCACHESのバックエンド (BACKEND) とファイルの保存先 (LOCATION) を指定します。

LOCATIONは書き込み可能な場所を指定します。 ちなみに存在しないディレクトリでもapp側で作成します。

dockerの場合はvolumesで指定した場所の必要があります。 BASE_DIR ではなくroot近くの場所になりそうです。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': BASE_DIR / 'tmp/django_cache',
        # 'KEY_PREFIX': 'prefix',
    }
}

開発・本番といった複数のサーバーを起動していて、それぞれのキャッシュ結果を区別したい時は KEY_PREFIX を設定できます。

参考: Cache key prefixing

キャッシュを保存・呼び出す

バックエンドの仕組みは関係なく、 django.core.cache.cache からキャッシュを利用できます。

値のセットにはオプションとして timeout と version も設定できます。

cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)
cache.get(key)

参考: Basic usage

FileBasedCache の仕組み

ソースコード ( django/filebased.py at main · django/django · GitHub ) を確認しながら仕組みを理解していきます。

実際に下記のようにキャッシュをセットしました。

cache.set('text', 'cache_text')

.djcache ファイルが指定した保存場所に確認できます。 読み込み・書き込み権限はあるみたいです。

1つのkeyごとに1つのファイルが作成されます。

ls -al BASE_DIR/tmp/django_cache
-rw-------  1 USER  staff  49 Oct DD HH:MM 4720993faefa6c12c92c4b03540e3e17.djcache

ファイル名

この長いファイル名は MD5 で暗号化された key です。

ですが、そのままkeyを渡しただけでは再現しません。

from django.utils.crypto import md5
md5("text".encode(), usedforsecurity=False).hexdigest()
'1cb251ec0d568de6a929b520c4aed8d1'

ファイル名のオプションとして prefix と version が指定できます。

つまり key, prefix, version の3つをフォーマットした文字列をファイル名とします。

フォーマット方法は KEY_FUNCTION で設定できますが、デフォルトでは django.core.cache.backends.base.default_key_func を使っています。 具体的には "%s:%s:%s" % (key_prefix, version, key) です。

keyprefix のデフォルトは '' (空文字)、 versionのデフォルトは 1 なので、その通り試してみると無事ファイル名が再現しました。

md5(":1:text".encode(), usedforsecurity=False).hexdigest()
'4720993faefa6c12c92c4b03540e3e17'

保存方法

.djcache ファイルは読み込みできるようなので、value を確認します。

ファイルは timeout (expired) の情報 と value を持ちます。

timeout は pickle, value は pickle と zlib で圧縮しています。

その順に読み込むと数値 (エポック秒) とvalueが取得できました。

with open("BASE_DIR/tmp/django_cache/4720993faefa6c12c92c4b03540e3e17.djcache", "rb")as f:
     exp = pickle.load(f)
     value = pickle.loads(zlib.decompress(f.read()))

print(exp, value)
>> 16670XXXXX.XXXXX cache_text

ちなみにデフォルトのtimeoutは300 second なので、永続化の場合 None を設定する必要があります

参考: TIMEOUT

参考サイト