Problema com cache do mod_wsgi

Certo dia terminei um projeto em Django e após vários testes finalmente foi ao ar. Feito isso, liguei para o cliente e marcamos uma reunião para o dia seguinte. Na reunião, conversa vai, conversa vem, o sistema é exibido, tudo funcionando, cliente feliz, eu feliz, tudo ótimo e maravilhoso. Eis então que o cliente sugere uma pequena alteração: Alterar uma mensagem emitida ao usuário. Tarefa simples, pensei. Vou abrir o arquivo e alterar uma string. Pronto! Nada complicado ou demorado que exija marcar uma nova reunião para apresentar essa mudança. Em casa eu atualizo o arquivo e fica tudo como está. Erro meu ter pensado assim. Após a modificação ter sido feita, o sistema não quis atualizar por nada. Apertei Crtl+F5, limpei o cache do navegador, testei em outros navegadores… e nada. Só podia ser cache no servidor de hospedagem. Então deletei todos os .pyc e mesmo assim não funcionou. A coisa era do mal mesmo.
Bom, expliquei ao cliente o que estava acontecendo e ganhei algum tempo para tentar resolver. Percebi que alterações feitas no código da aplicação não eram atualizadas em tempo real. Em outras palavras, se seu projeto já está no servidor de produção usando o mod_wsgi e você alterar o código de uma view ou model, a atualização não será refletida no exato momento.
Não me aprofundei no assunto mas aparentemente o mod_wsgi provê um sistema de cache que leva horas para verificar se houve alterações no código da aplicação. De fato, essa é uma atitude inteligente, pois uma vez que o projeto está em ambiente de produção, nenhuma alteração deveria ser feita naquele espaço. As alterações deveriam ser feitas no ambiente de desenvolvimento e só depois de passar nos testes seria levada ao ambiente de produção.
Então fui pedir ajuda ao oráculo (google) e, após algum tempo, encontrei um site contendo um script que me ajudou bastante naquele momento.

Crie um arquivo chamado monitor.py e ponha-o no diretório do projeto. Abaixo o conteúdo do arquivo:

import os
import sys
import time
import signal
import threading
import atexit
import Queue
 
_interval = 1.0
_times = {}
_files = []
 
_running = False
_queue = Queue.Queue()
_lock = threading.Lock()
 
def _restart(path):
  _queue.put(True)
  prefix = 'monitor (pid=%d):' % os.getpid()
  print >> sys.stderr, '%s Change detected to \'%s\'.' % (prefix, path)
  print >> sys.stderr, '%s Triggering process restart.' % prefix
  os.kill(os.getpid(), signal.SIGINT)
 
def _modified(path):
  try:
    if not os.path.isfile(path):
      return path in _times
 
    mtime = os.stat(path).st_mtime
    if path not in _times:
      _times[path] = mtime
 
    if mtime != _times[path]:
      return True
  except:
    return True
 
  return False
 
def _monitor():
  while 1:
    for module in sys.modules.values():
      if not hasattr(module, '__file__'):
        continue
      path = getattr(module, '__file__')
      if not path:
        continue
      if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']:
        path = path[:-1]
      if _modified(path):
        return _restart(path)
    for path in _files:
      if _modified(path):
        return _restart(path)
 
    try:
      return _queue.get(timeout=_interval)
    except:
      pass
 
_thread = threading.Thread(target=_monitor)
_thread.setDaemon(True)
 
def _exiting():
  try:
    _queue.put(True)
  except:
    pass
  _thread.join()
 
atexit.register(_exiting)
 
def track(path):
  if not path in _files:
    _files.append(path)
 
def start(interval=1.0):
  global _interval
  if interval < _interval:
    _interval = interval
 
  global _running
  _lock.acquire()
  if not _running:
    prefix = 'monitor (pid=%d):' % os.getpid()
    print >> sys.stderr, '%s Starting change monitor.' % prefix
    _running = True
    _thread.start()
  _lock.release()

Para finalizar a configuração, altere o arquivo de configuração do mod_wsgi de modo a conter as duas últimas linhas do código abaixo:

import os, sys
sys.path.append('/home/usuario/www/apps_wsgi')
os.environ['DJANGO_SETTINGS_MODULE']='meuprojeto.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
 
import meuprojeto.monitor
meuprojeto.monitor.start(interval=1.0)

Feito isso as alterações no código serão atualizadas no navegador em tempo real.
Lamento não inserir uma referência para o site do qual copiei o script, pois já faz algum tempo e não me lembro mais o nome nem a URL.

Links Relacionados

http://blog.dscpl.com.au/2009/02/source-code-reloading-with-modwsgi-on.html
http://blog.dscpl.com.au/2008/12/using-modwsgi-when-developing-django.html

You can leave a response, or trackback from your own site.

5 Responses to “Problema com cache do mod_wsgi”

  1. Walter Cruz says:

    Cara, com mod_wsgi, basta vc criar um arquivo chamado /tmp/restart.txt no seu projeto que o wsgi recarrega o projeto. O lance é que o deploy de coisas Python (independente de framework) não será igual ao uso de PHP,onde basta substituir os arquivos e estará tudo ok.

    É isso!

  2. admin says:

    @Walter
    Obrigado pela dica!

  3. Walter Cruz says:

    Olha só eu divulgando informação incorreta… Pra recarregar o projeto, basta dar um touch no .wsgi que faz a interface com o apache! Abraço.

  4. Muito legal, parabéns pela dica.

    Conforme a dica do Walter Cruz, parece que você foi meio pelo caminho das pedras. Mas foi algo que funcionou legal.

    No final das contas, o conhecimento e a experiência valeu a pena.

    Parabéns e obrigado por compartilhar. :)

  5. Muito boa a dica, ainda estou pensando em tirar o mod_python para usar o mod_wsgi, tenho algumas dúvidas ainda, e ter essas dicas ja nos ajuda se tivermos problemas.

    Abraços

Leave a Reply

Powered by WordPress | Shop the Best Verizon Wireless Deals. | Thanks to Best CD Rates, Credit Cards and Credit card