On Sep 8, 2014, at 8:12 PM, Mike Bayer <mba...@redhat.com> wrote:

> Hi All - 
> 
> Joe had me do some quick memory profiling on nova, just an FYI if anyone 
> wants to play with this technique, I place a little bit of memory profiling 
> code using Guppy into nova/api/__init__.py, or anywhere in your favorite app 
> that will definitely get imported when the thing first runs:
> 
> from guppy import hpy
> import signal
> import datetime
> 
> def handler(signum, frame):
>     print "guppy memory dump"
> 
>     fname = "/tmp/memory_%s.txt" % 
> datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
>     prof = hpy().heap()
>     with open(fname, 'w') as handle:
>         prof.dump(handle)
>     del prof
> 
> signal.signal(signal.SIGUSR2, handler)

This looks like something we could build into our standard service startup 
code. Maybe in 
http://git.openstack.org/cgit/openstack/oslo-incubator/tree/openstack/common/service.py
 for example?

Doug

> 
> 
> 
> Then, run nova-api, run some API calls, then you hit the nova-api process 
> with a SIGUSR2 signal, and it will dump a profile into /tmp/ like this:
> 
> http://paste.openstack.org/show/108536/
> 
> Now obviously everyone is like, oh boy memory lets go beat up SQLAlchemy 
> again…..which is fine I can take it.  In that particular profile, there’s a 
> bunch of SQLAlchemy stuff, but that is all structural to the classes that are 
> mapped in Nova API, e.g. 52 classes with a total of 656 attributes mapped.   
> That stuff sets up once and doesn’t change.   If Nova used less ORM,  e.g. 
> didn’t map everything, that would be less.  But in that profile there’s no 
> “data” lying around.
> 
> But even if you don’t have that many objects resident, your Python process 
> might still be using up a ton of memory.  The reason for this is that the 
> cPython interpreter has a model where it will grab all the memory it needs to 
> do something, a time consuming process by the way, but then it really doesn’t 
> ever release it  (see 
> http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm
>  for the “classic” answer on this, things may have improved/modernized in 2.7 
> but I think this is still the general idea).
> 
> So in terms of SQLAlchemy, a good way to suck up a ton of memory all at once 
> that probably won’t get released is to do this:
> 
> 1. fetching a full ORM object with all of its data
> 
> 2. fetching lots of them all at once
> 
> 
> So to avoid doing that, the answer isn’t necessarily that simple.   The quick 
> wins to loading full objects are to …not load the whole thing!   E.g. 
> assuming we can get Openstack onto 0.9 in requirements.txt, we can start 
> using load_only():
> 
> session.query(MyObject).options(load_only(“id”, “name”, “ip”))
> 
> or with any version, just load those columns - we should be using this as 
> much as possible for any query that is row/time intensive and doesn’t need 
> full ORM behaviors (like relationships, persistence):
> 
> session.query(MyObject.id, MyObject.name, MyObject.ip)
> 
> Another quick win, if we *really* need an ORM object, not a row, and we have 
> to fetch a ton of them in one big result, is to fetch them using yield_per():
> 
>    for obj in session.query(MyObject).yield_per(100):
>         # work with obj and then make sure to lose all references to it
> 
> yield_per() will dish out objects drawing from batches of the number you give 
> it.   But it has two huge caveats: one is that it isn’t compatible with most 
> forms of eager loading, except for many-to-one joined loads.  The other is 
> that the DBAPI, e.g. like the MySQL driver, does *not* stream the rows; 
> virtually all DBAPIs by default load a result set fully before you ever see 
> the first row.  psycopg2 is one of the only DBAPIs that even offers a special 
> mode to work around this (server side cursors).
> 
> Which means its even *better* to paginate result sets, so that you only ask 
> the database for a chunk at a time, only storing at most a subset of objects 
> in memory at once.  Pagination itself is tricky, if you are using a naive 
> LIMIT/OFFSET approach, it takes awhile if you are working with a large 
> OFFSET.  It’s better to SELECT into windows of data, where you can specify a 
> start and end criteria (against an indexed column) for each window, like a 
> timestamp.
> 
> Then of course, using Core only is another level of fastness/low memory.  
> Though querying for individual columns with ORM is not far off, and I’ve also 
> made some major improvements to that in 1.0 so that query(*cols) is pretty 
> competitive with straight Core (and Core is…well I’d say becoming visible in 
> raw DBAPI’s rear view mirror, at least….).
> 
> What I’d suggest here is that we start to be mindful of memory/performance 
> patterns and start to work out naive ORM use into more savvy patterns; being 
> aware of what columns are needed, what rows, how many SQL queries we really 
> need to emit, what the “worst case” number of rows will be for sections that 
> really need to scale.  By far the hardest part is recognizing and 
> reimplementing when something might have to deal with an arbitrarily large 
> number of rows, which means organizing that code to deal with a “streaming” 
> pattern where you never have all the rows in memory at once - on other 
> projects I’ve had tasks that would normally take about a day, but in order to 
> organize it to “scale”, took weeks - such as being able to write out a 1G XML 
> file from a database (yes, actual use case - not only do you have to stream 
> your database data, but you also have to stream out your DOM nodes for which 
> I had to write some fancy SAX extensions).   
> 
> I know that using the ORM makes SQL development “easy”, and so many anti-ORM 
> articles insist that this lulls us all into not worrying about what is 
> actually going on (as much as SQLAlchemy eschews that way of working)…but I 
> remain optimistic that it *is* possible to use tools that save a vast amount 
> of effort, code verbosity and inconsistency that results from doing 
> everything “by hand”, while at the same time not losing our ability to 
> understand how we’re talking to the database.   It’s a cake and eat it too, 
> situation, I know.
> 
> This is already what I’m here to contribute on, I’ve been working out some 
> new SQLAlchemy patterns that hopefully will help, but in the coming weeks I 
> may try to find time to spot some more of these particular things within 
> current Nova code without getting too much into a total rewrite as of yet.
> 
> 
> 
> 
> 
> On Sep 8, 2014, at 6:24 PM, Joe Gordon <joe.gord...@gmail.com> wrote:
> 
>> Hi All,
>> 
>> We have recently started seeing assorted memory issues in the gate including 
>> the oom-killer [0] and libvirt throwing memory errors [1]. Luckily we run ps 
>> and dstat on every devstack run so we have some insight into why we are 
>> running out of memory. Based on the output from job taken at random [2][3] a 
>> typical run consists of:
>> 
>> * 68 openstack api processes alone
>> * the following services are running 8 processes (number of CPUs on test 
>> nodes)
>>   * nova-api (we actually run 24 of these, 8 compute, 8 EC2, 8 metadata)
>>   * nova-conductor
>>   * cinder-api
>>   * glance-api
>>   * trove-api
>>   * glance-registry
>>   * trove-conductor
>> * together nova-api, nova-conductor, cinder-api alone take over 45 %MEM 
>> (note: some of that is memory usage is counted multiple times as RSS 
>> includes shared libraries)
>> * based on dstat numbers, it looks like we don't use that much memory before 
>> tempest runs, and after tempest runs we use a lot of memory.
>> 
>> Based on this information I have two categories of questions:
>> 
>> 1) Should we explicitly set the number of workers that services use in 
>> devstack? Why have so many workers in a small all-in-one environment? What 
>> is the right balance here?
>> 
>> 2) Should we be worried that some OpenStack services such as nova-api, 
>> nova-conductor and cinder-api take up so much memory? Does there memory 
>> usage keep growing over time, does anyone have any numbers to answer this? 
>> Why do these processes take up so much memory?
>> 
>> best,
>> Joe
>> 
>> 
>> [0] 
>> http://logstash.openstack.org/#eyJzZWFyY2giOiJtZXNzYWdlOlwib29tLWtpbGxlclwiIiwiZmllbGRzIjpbXSwib2Zmc2V0IjowLCJ0aW1lZnJhbWUiOiIxNzI4MDAiLCJncmFwaG1vZGUiOiJjb3VudCIsInRpbWUiOnsidXNlcl9pbnRlcnZhbCI6MH0sInN0YW1wIjoxNDEwMjExMjA5NzY3fQ==
>> [1] https://bugs.launchpad.net/nova/+bug/1366931
>> [2] http://paste.openstack.org/show/108458/
>> [3] 
>> http://logs.openstack.org/83/119183/4/check/check-tempest-dsvm-full/ea576e7/logs/screen-dstat.txt.gz
>> _______________________________________________
>> OpenStack-dev mailing list
>> OpenStack-dev@lists.openstack.org
>> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
> 
> _______________________________________________
> OpenStack-dev mailing list
> OpenStack-dev@lists.openstack.org
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

_______________________________________________
OpenStack-dev mailing list
OpenStack-dev@lists.openstack.org
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

Reply via email to