12

I followed a tutorial of Flask-Cache and tried to implement it myself. Given the following example, why would Flask not cache the time?

from flask import Flask
import time

app = Flask(__name__)
cache = Cache(config={'CACHE_TYPE': 'simple'})
cache.init_app(app)

@app.route('/time')
@cache.cached(timeout=50, key_prefix='test')
def test():
   return time.ctime()

Output is always the current time.

It seems like the cache is recreated every single request. What am I doing wrong?

Edit: I execute the following python-file with Python 2.7.6:

def runserver():
    port = int(os.environ.get('PORT', 5000))
    Triangle(app)
    app.run(host='0.0.0.0', port=port, processes=5)


if __name__ == '__main__':
    runserver()
Frame91
  • 3,670
  • 8
  • 45
  • 89
  • I can't reproduce this; when running with Flask with the bundled `app.run()` development server, the time is cached. How are you running this? – Martijn Pieters Oct 06 '15 at 17:09
  • Note that `SimpleCache` is a *global dictionary*. If you are using a WSGI server that uses multiprocessing to scale out, you'll get separate copies and they'll appear not to cache. – Martijn Pieters Oct 06 '15 at 17:10
  • Thanks Martijn, I am running it locally and not deploying it to any wsgi server. I added my runscript – Frame91 Oct 06 '15 at 17:13

1 Answers1

23

You are using the SimpleCache setup:

cache = Cache(config={'CACHE_TYPE': 'simple'})

This uses a single global dictionary to hold the cache, and this in turn will only work if you are using a WSGI server that uses one Python interpreter to serve all your WSGI requests. If you use a WSGI server that uses separate child processes to handle requests, you'll get a new copy of that dictionary each time and nothing is cached, effectively.

The code works fine when run with the built-in app.run() development server, given you don't use the processes argument.

Your update shows that you run the server with 5 separate processes. Each process will get its own dictionary, and the cache is not shared between them. Use a different caching backend instead, like filesystem:

cache = Cache(config={'CACHE_TYPE': 'filesystem', 'CACHE_DIR': '/tmp'})
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • You are right! What do I have to change if I want to keep the processes argument? – Frame91 Oct 06 '15 at 17:14
  • Use a different backend, one that can be shared between processes. The `filesystem` is one that doesn't require additional dependencies. – Martijn Pieters Oct 06 '15 at 17:15
  • 1
    Thank you so much for the perfect answer! I wouldn't have figured it out by myself! – Frame91 Oct 06 '15 at 17:16
  • for WSGI if I am telling processes from .ini file will app.run(host='0.0.0.0', port=port, processes=5) change the case ? also cache_dir can be visible ?? – Beqa Bukhradze Jun 10 '18 at 20:02
  • @BeqaBukhradze: I'm not sure what you are asking, really. It doesn't matter how you are configuring your WSGI server, if you use separate processes you are using separate processes. – Martijn Pieters Jun 10 '18 at 21:47
  • is it better configure it from flask or from uwsgi – Beqa Bukhradze Jun 11 '18 at 04:14
  • @BeqaBukhradze: those things would be configured in the *WSGI server*. If you are using uwsgi as the server, then configure the host, port and concurrency settings *there*. Flask comes with a built-in WSGI server to ease development, but this server should never be used in production (it is not designed to handle real-world loads). – Martijn Pieters Jun 11 '18 at 12:05
  • @MartijnPieters [link](https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uwsgi-and-nginx-on-ubuntu-16-04#configure-uwsgi) Here when calling app.run() can i call app.run(threaded=true) or it is not necessary ? – Beqa Bukhradze Jun 11 '18 at 12:17
  • 1
    @BeqaBukhradze: when using uWSGI, that part *doesn't run*. The `if __name__ == '__main__':` test is not true in that case. `app.run()` runs the Flask built-in WSGI server, not uWSGI. – Martijn Pieters Jun 11 '18 at 14:59