através de minhas investigações recentes, desencadeadas por problemas semelhantes com htcacheclean, concluí que o principal problema com a limpeza de caches grandes ou profundos, especialmente aqueles que envolvem cabeçalhos Vary, é um problema com o design do próprio utilitário.
com base em procurar no código-fonte e observar a saída de strace -e trace = unlink, a abordagem geral parece ser a seguinte:
- iterar em todos os diretórios de nível superior (/ htcache / B / x /, acima)
- exclua quaisquer arquivos .header e .data para entradas já expiradas
- reúna os metadados para todas as entradas aninhadas (/htcache/B/x/i_iGfmmHhxJRheg8NHcQ.header.vary/A/W/oGX3MAV3q0bWl30YmA_A.header, acima)
- iterar todos os metadados de entrada aninhada e eliminar aqueles com tempo de resposta, modificador .header ou mod .data no futuro
- iterar todos os metadados de entrada aninhada e eliminar aqueles que expiraram
- iterar todos os metadados de entrada aninhados para encontrar o mais antigo; limpe-o; repita
e qualquer um dos últimos três passos retornará da sub-rotina de limpeza quando o tamanho do cache cair abaixo do limite definido.
Portanto, com um cache de crescimento rápido e / ou grande, a taxa de crescimento durante o tempo estendido necessário para a etapa 1 pode ser facilmente insuperável mesmo depois que você progredir para as etapas # 2- # 4.
agravando ainda mais o problema, se você ainda não tiver satisfeito os limites de tamanho até o final da etapa 2, o fato de ter que percorrer todos os metadados das entradas aninhadas para localizar as mais antigas, para somente exclua essa entrada única e faça a mesma coisa novamente, o que significa que o cache pode crescer novamente mais rápido do que você conseguirá cortar.
/* process remaining entries oldest to newest, the check for an emtpy
* ring actually isn't necessary except when the compiler does
* corrupt 64bit arithmetics which happend to me once, so better safe
* than sorry
*/
while (sum > max && !interrupted && !APR_RING_EMPTY(&root, _entry, link)) {
oldest = APR_RING_FIRST(&root);
for (e = APR_RING_NEXT(oldest, link);
e != APR_RING_SENTINEL(&root, _entry, link);
e = APR_RING_NEXT(e, link)) {
if (e->dtime < oldest->dtime) {
oldest = e;
}
}
delete_entry(path, oldest->basename, pool);
sum -= oldest->hsize;
sum -= oldest->dsize;
entries--;
APR_RING_REMOVE(oldest, link);
}
a solução?
obviamente discos rápidos (er) ajudariam. mas não está claro para mim quanto de um aumento na produção de IO seria necessário para superar os problemas inerentes à abordagem atual adotada pelo htcacheclean. nenhuma escavação contra os criadores ou mantenedores, mas com certeza parece que esse design não foi testado ou nunca esperado ter um bom desempenho contra caches amplos, profundos e de rápido crescimento.
mas o que parece funcionar, e eu ainda estou confirmando agora, é acionar o htcacheclean de dentro de um script bash que ele próprio passa pelos diretórios de nível superior.
#!/bin/bash
# desired cache size in integer gigabytes
SIZE=12;
# divide that by the number of top-level directories (4096),
# to get the per-directory limit, in megabytes
LIMIT=$(( $SIZE * 1024 * 1024 * 1024 / 4096 / 1024 / 1024 ))M;
while true;
do
for i in /htcache/*/*;
do
htcacheclean -t -p$i -l$LIMIT;
done;
done;
Basicamente, essa abordagem permite que você chegue às etapas de eliminação (# 2- # 4) com muito mais rapidez e frequência, mesmo que apenas para um pequeno subconjunto de entradas. Isso significa que você tem uma chance de eliminar conteúdo a uma taxa mais rápida do que está sendo adicionada ao cache. novamente, parece estar funcionando para nós, mas eu só testei por alguns dias. e nossos alvos e crescimento de cache parecem estar no mesmo nível do seu, mas, no final das contas, sua milhagem pode variar.
é claro que o ponto principal deste post é que talvez seja útil para alguém que se depara com esta questão da mesma forma que eu fiz.