A T
A T

Reputation: 13836

API started, db killed, db started, peewee.InterfaceError connection already closed

Starting app.py, then killing the database and hitting /api/foo gives me:

peewee.OperationalError: could not connect to server: Connection refused

Bringing the database back up gives me and hitting /api/foo gives me:

peewee.OperationalError: terminating connection due to administrator command\nSSL connection has been closed unexpectedly\n

And hitting /api/foo again gives me:

peewee.InterfaceError: connection already closed

Test case

test_case/__init__.py

#!/usr/bin/env python

from os import environ

from bottle import Bottle, request, response
from playhouse.db_url import connect

bottle_api = Bottle()
db = connect(environ['RDBMS_URI'])

from test_case.foo.models import Foo

db.connect()  # Not needed, but do want to throw errors ASAP
db.create_tables([Foo], safe=True)  # Create tables (if they don't exist)

from test_case.foo.routes import foo_api

bottle_api.merge(foo_api)

bottle_api.catchall = False


@bottle_api.hook('before_request')
def _connect_db():
    print 'Connecting to db'
    db.connect()


@bottle_api.hook('after_request')
def _close_db():
    print 'Closing db'
    if not db.is_closed():
        db.close()


def error_catcher(environment, start_response):
    try:
        return bottle_api.wsgi(environment, start_response)
    except Exception as e:
        environment['PATH_INFO'] = '/api/error'
        environment['api_error'] = e
        return bottle_api.wsgi(environment, start_response)


@bottle_api.route('/api/error')
def global_error():
    response.status = 500
    return {'error': (lambda res: res[res.find("'") + 1:res.rfind("'")])(
                      str(request.environ['api_error'].__class__)),
            'error_message': request.environ['api_error'].message}

test_case/__main__.py

from __init__ import bottle_api
# Or `from __init__ import bottle_api`; `from bottle import run`;
# Then `run(error_catcher, port=5555)`

bottle_api.run(port=5555)

test_case/foo/__init__.py

test_case/foo/models.py

from peewee import Model, CharField

from test_case import db

class Foo(Model):
    id = CharField(primary_key=True)

    class Meta(object):
        database = db

test_case/foo/routes.py

from bottle import Bottle
from playhouse.shortcuts import model_to_dict

from test_case.foo.models import Foo

foo_api = Bottle()


@foo_api.get('/api/foo')
def retrieve_foos():
    return {'foos': tuple(model_to_dict(foo) for foo in Foo.select())}

Github gist for easy cloning.

Upvotes: 0

Views: 3023

Answers (1)

coleifer
coleifer

Reputation: 26215

Update:

I believe the problem lies in how you've structured your imports and the way python loads and caches modules in sys.path.

I think that one of your modules is being imported and loaded twice and different parts of the codebase use different instances of the module.

Thus, the views in foo.routes, are using one instance of the database object, while the connection hooks are using another.

Instead of from __init__, what about trying from test_case import bottle_api? That is the one import statement that jumps out at me as a possible culprit.


I added the following to your code so I could run it from the command-line:

if __name__ == '__main__':
    api.run()

Then I made a request to /api/foo and saw some fake data. I stopped the Postgresql server and got this error:

Traceback (most recent call last):
  File "/usr/lib64/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/home/charles/tmp/scrap/bottlez/lib/python2.7/site-packages/bottle.py", line 979, in __call__
    return self.wsgi(environ, start_response)
  File "/home/charles/tmp/scrap/bottlez/lib/python2.7/site-packages/bottle.py", line 954, in wsgi
    out = self._cast(self._handle(environ))
  File "/home/charles/tmp/scrap/bottlez/lib/python2.7/site-packages/bottle.py", line 857, in _handle
    self.trigger_hook('before_request')
  File "/home/charles/tmp/scrap/bottlez/lib/python2.7/site-packages/bottle.py", line 640, in trigger_hook
    return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
  File "bt.py", line 31, in _connect_db
    db.connect()
  File "/home/charles/tmp/scrap/bottlez/src/peewee/peewee.py", line 2967, in connect
    self.initialize_connection(self.__local.conn)
  File "/home/charles/tmp/scrap/bottlez/src/peewee/peewee.py", line 2885, in __exit__
    reraise(new_type, new_type(*exc_value.args), traceback)
  File "/home/charles/tmp/scrap/bottlez/src/peewee/peewee.py", line 2965, in connect
    **self.connect_kwargs)
  File "/home/charles/tmp/scrap/bottlez/src/peewee/peewee.py", line 3279, in _connect
    conn = psycopg2.connect(database=database, **kwargs)
  File "/home/charles/tmp/scrap/bottlez/lib/python2.7/site-packages/psycopg2/__init__.py", line 164, in connect
    conn = _connect(dsn, connection_factory=connection_factory, async=async)
OperationalError: could not connect to server: Connection refused
    Is the server running on host "localhost" (::1) and accepting
    TCP/IP connections on port 5432?
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?

When I restarted the server and made a subsequent request I got a normal response with my test data.

So, in short, I'm not sure what I may be missing but the code seems to be working correctly to me.

Postgresql 9.4, psycopg2 2.6, python 2.7.9, peewee 2.6.0

Upvotes: 1

Related Questions