Packages
phoenix_kit
2.60.1
2.60.3
2.60.2
2.60.1
2.60.0
2.59.0
2.58.0
2.57.1
2.57.0
2.56.1
2.56.0
2.55.1
2.55.0
2.54.2
2.54.1
2.54.0
2.53.0
2.52.2
2.52.1
2.52.0
2.51.0
2.50.0
2.49.1
2.49.0
2.48.0
2.47.0
2.46.0
2.45.0
2.44.0
2.43.1
2.43.0
2.42.1
2.42.0
2.41.6
2.41.4
2.41.3
2.41.2
2.41.1
2.41.0
2.40.1
2.40.0
2.39.0
2.38.1
2.38.0
2.37.5
2.37.4
2.37.3
2.37.2
2.37.1
2.37.0
2.36.1
2.36.0
2.35.0
2.34.0
2.33.0
2.32.1
2.32.0
2.31.1
2.31.0
2.30.0
2.29.1
2.29.0
2.28.2
2.28.1
2.28.0
2.27.2
2.27.1
2.27.0
2.26.1
2.26.0
2.25.0
2.24.0
2.23.3
2.23.2
2.23.1
2.23.0
2.22.24
2.22.23
2.22.22
2.22.21
2.22.20
2.22.19
2.22.18
2.22.17
2.22.16
2.22.15
2.22.14
2.22.13
2.22.12
2.22.11
2.22.10
2.22.9
2.22.8
2.22.7
2.22.6
2.22.5
2.22.4
2.22.3
2.22.2
2.22.1
2.22.0
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.0
2.19.0
2.18.1
2.18.0
2.17.0
2.16.0
2.15.1
2.15.0
2.14.2
2.14.1
2.14.0
2.13.19
2.13.18
2.13.17
2.13.16
2.13.15
2.13.13
2.13.12
2.13.11
2.13.10
2.13.9
2.13.8
2.13.7
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.1
2.8.0
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.1
2.0.0
1.7.236
1.7.235
1.7.234
1.7.233
1.7.232
1.7.231
1.7.230
1.7.229
1.7.228
1.7.227
1.7.226
1.7.225
1.7.224
1.7.223
1.7.222
1.7.221
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
1.7.210
1.7.209
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/storage/key_layout.ex
defmodule PhoenixKit.Modules.Storage.KeyLayout do
@moduledoc """
How deep a storage profile fans its keys out (`StorageProfile.key_levels`, V216).
A file's folder is `<prefix>/<md5>`, with `levels` two-character hash folders
between them:
0 lib-1a2b3c/22e8b90781bab72967f9d5410b4299f9/…
1 lib-1a2b3c/22/22e8b90781bab72967f9d5410b4299f9/… (the layout so far)
2 lib-1a2b3c/22/e8/22e8b90781bab72967f9d5410b4299f9/…
3 lib-1a2b3c/22/e8/b9/22e8b90781bab72967f9d5410b4299f9/…
Each hash folder has 256 possible names, and MD5 spreads files evenly over
them, so `n` files leave about `n / 256^levels` file folders in each folder at
the deepest level. That number is what a local disk feels (listing, backups,
`rsync`); an object store has no folders and does not care. The layout applies
to new uploads: a file's folder is stored on its row, so nothing already
uploaded moves.
"""
@levels [0, 1, 2, 3]
@default 1
@fanout 256
# Folders per directory up to which listing and backing up stay quick, and up
# to which a disk still copes. Judgement calls, in one place.
@comfortable 5_000
@tolerable 50_000
@doc "The layouts a profile may choose: the number of hash folders."
@spec levels() :: [0..3]
def levels, do: @levels
@doc "The layout every profile had before it could be chosen (V216)."
@spec default() :: 1
def default, do: @default
@doc "Whether `levels` is a layout."
@spec valid?(term()) :: boolean()
def valid?(levels), do: levels in @levels
@doc """
The folder of a file: `prefix`, `levels` hash folders, then the MD5.
iex> PhoenixKit.Modules.Storage.KeyLayout.path("lib-1a2b", "22e8b907", 0)
"lib-1a2b/22e8b907"
iex> PhoenixKit.Modules.Storage.KeyLayout.path("lib-1a2b", "22e8b907", 1)
"lib-1a2b/22/22e8b907"
iex> PhoenixKit.Modules.Storage.KeyLayout.path("lib-1a2b", "22e8b907", 2)
"lib-1a2b/22/e8/22e8b907"
"""
@spec path(String.t(), String.t(), non_neg_integer()) :: String.t()
def path(prefix, md5, levels) when levels in @levels do
folders = for i <- 0..(levels - 1)//1, do: String.slice(md5, i * 2, 2)
Enum.join([prefix | folders] ++ [md5], "/")
end
def path(prefix, md5, _levels), do: path(prefix, md5, @default)
@doc """
About how many file folders the busiest folder holds once a library has `files`
files, under `levels`.
iex> PhoenixKit.Modules.Storage.KeyLayout.per_folder(1_000_000, 1)
3907
iex> PhoenixKit.Modules.Storage.KeyLayout.per_folder(1_000_000, 0)
1000000
"""
@spec per_folder(non_neg_integer(), 0..3) :: non_neg_integer()
def per_folder(files, levels), do: ceil(files / Integer.pow(@fanout, levels))
@doc "How a folder of `per_folder` entries fares: `:comfortable`, `:slow` or `:too_many`."
@spec rating(non_neg_integer()) :: :comfortable | :slow | :too_many
def rating(per_folder) when per_folder <= @comfortable, do: :comfortable
def rating(per_folder) when per_folder <= @tolerable, do: :slow
def rating(_per_folder), do: :too_many
@doc "The library sizes the Storage profiles tab compares."
@spec sizes() :: [pos_integer()]
def sizes, do: [10_000, 100_000, 1_000_000, 10_000_000, 100_000_000]
@doc """
One row per library size: `{files, per_folder, rating}` under `levels`.
"""
@spec table(0..3) :: [{pos_integer(), non_neg_integer(), atom()}]
def table(levels) do
for files <- sizes() do
per = per_folder(files, levels)
{files, per, rating(per)}
end
end
@doc "The number of files at which `levels` stops being comfortable."
@spec comfortable_up_to(0..3) :: pos_integer()
def comfortable_up_to(levels), do: @comfortable * Integer.pow(@fanout, levels)
end