Tuesday, June 27, 2017

Backing up or dumping a memcached server

I was needing to move from an old cache server to a larger one, but I wanted to do it without flushing cache.

The first thing I came across was this "memcached-tool" which has a dump command: https://github.com/memcached/memcached/blob/master/scripts/memcached-tool

There's another article that mentions using memdump and memcat: http://www.dctrwatson.com/2010/12/how-to-dump-memcache-keyvalue-pairs-fast/

Unfortunately, those methods only dumped a few mb of data. This post explains why: https://stackoverflow.com/a/13941700

You can only dump one page per slab class (1MB of data)
So, I ended up writing a script that loops through the expected cache keys, gets the data in cache, then sets the data in the new cache server.

Monday, June 12, 2017

Gunicorn - "Resource temporarily unavailable"

Are you seeing this error in your logs while your server is under high load?:
[error] 10#0: *14843 connect() to unix:/tmp/gunicorn.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 96.44.145.186, server: , request: "GET / HTTP/1.0", upstream: "http://unix:/tmp/gunicorn.sock:/", host: "45.55.46.84"
I ended up making an example dockerfile with nginx + gunicorn + flask to reproduce this problem: https://github.com/pawl/somaxconn_test

Bumping the "net.core.somaxconn" setting ended up fixing it.

Friday, March 17, 2017

SQLAlchemy - empty lists + in_() causing crazy queries (before 1.2.0)

Before SQLAlchemy 1.2.0, if you use an empty list with in_(), it will emit some crazy SQL that will query your entire table. The best solution is probably to upgrade to SQLAlchemy 1.2.0.


Monday, March 13, 2017

The Robustness Principle

I learned about this at a talk called "Implementing Evolvable APIs" at SXSW: https://en.wikipedia.org/wiki/Robustness_principle

For example, making an API that throws errors when an unexpected parameter is provided is a bad idea. What if you need to make changes to the client to add the new parameter? You will need to make sure you deploy the code on the server side first, otherwise it will cause errors.

Tuesday, February 14, 2017

SQLAlchemy - DISTINCT, LIMIT, or OFFSET Causing Subqueries

This section at the bottom of The Zen of Eager loading is really important:
When using joined eager loading, if the query contains a modifier that impacts the rows returned externally to the joins, such as when using DISTINCT, LIMIT, OFFSET or equivalent, the completed statement is first wrapped inside a subquery, and the joins used specifically for joined eager loading are applied to the subquery. SQLAlchemy’s joined eager loading goes the extra mile, and then ten miles further, to absolutely ensure that it does not affect the end result of the query, only the way collections and related objects are loaded, no matter what the format of the query is.
I made an example to illustrate this here: https://gist.github.com/pawl/cb57e0ddbdd0b2e64b75e94116873367

On MySQL this can be responsible for some really poor query performance, because it can cause it to use temporary tables and filesort.

The best way I've found to prevent the subqueries is by first querying for the ids only, then running another query that includes all relations. For example:
ids = session.query(Product.id).limit(20)
Product.query.filter(Product.id.in_(ids))

Sunday, February 12, 2017

SQLAlchemy - "Base.query = db_session.query_property()"

I just saw this line in the SQLAlchemy flask patterns example:
Base.query = db_session.query_property()
I went searching for what it does and found this: https://eoyilmaz.blogspot.com/2014/01/i-feel-so-much-hungry-about-posting.html

Now I know how Flask-SQLAlchemy made queries work with Model.query instead of session.query(Model).

Saturday, February 11, 2017

SQLAlchemy - Lost connection to MySQL server during query

Here's was my situation:
  • The database was set up behind behind an AWS ELB and HAProxy. 
  • The idle connection timeout on the ELB was set to 60 mins.
  • All the relevant timeouts on HAProxy seemed to be set to 60 mins too. 
  • The pool_recycle in SQLAlchemy was set to 30 mins.
  • I was still seeing the occasional "Lost connection to MySQL server during query" when small queries were running after the connection had some time to sit around.
The solution ended up being setting my pool_recycle down to 5 mins, but I'm still not sure what was causing connections to time out after 5 mins.

There are definitely other things that can cause this problem too. For example, it can happen if your data exceeds max_allowed_packet. See this page for more details: https://dev.mysql.com/doc/refman/5.7/en/error-lost-connection.html

Most of this also applies for "MySQL server has gone away".

You should also make sure your pool_recycle is set lower than your 'interactive_timeout' and 'wait_timeout' properties in the mysql config file to the values you need.

Thursday, January 26, 2017

SQLAlchemy - Unexpected Lazy Loading

If you're seeing unexpected lazy loading on a lazy="joined" relationship in SQLAlchemy, it might be because you're accessing those relationships after you've already run session.commit(). By default, session.commit() will expire the data on your relationships, meaning it will try to fetch it again next time you try to access those attributes.

The relevant section of the docs for session.commit():
By default, the Session also expires all database loaded state on all ORM-managed attributes after transaction commit. This so that subsequent operations load the most recent data from the database. This behavior can be disabled using the expire_on_commit=False option to sessionmaker or the Session constructor.


Thursday, January 19, 2017

Python - David Beazley's "Understanding The Python GIL"

This talk explains a lot about Python's multithreading: http://www.dabeaz.com/python/UnderstandingGIL.pdf

In Python 2.7, it knows to switch to different thread whenever it starts waiting on IO. And, it will check if it needs to switch threads every 100 bytecode instructions.

Monday, January 16, 2017

SQLAlchemy - Is is necessary to commit after session.execute?

The docs for session.execute say:
Execute a SQL expression construct or string statement within the current transaction.
So, the answer is yes, you need to issue a commit after running session.execute().

Here's a code example along with the output from "echo=True" showing it begins the session but doesn't end it unless you run session.commit():