[Dbix-class] order_by interfering with has_many fetch

Peter Rabbitson rabbit+dbic at rabbit.us
Sat Nov 14 22:39:04 GMT 2015


On 11/14/2015 10:46 PM, Darin McBride wrote:
> ...
>   order_by => ['me.name'],
> ...
>
...
> DBIx::Class::ResultSet::_construct_results(): Unable to properly collapse
> has_many results in iterator mode due to order criteria - performed an eager
> cursor slurp underneath. Consider using ->all() instead at /data/Lacuna-
> Server/lib/Lacuna/DB/Result/Empire.pm line 622
>
> The earlier message implied that it would be fixed in 0.082700_05, but I'm
> using 0.082810 at the moment.  So, either it wasn't fixed, or I'm encountering
> something different.

It is (I suspect) a different issue, and if the case - the warning (it 
is not an error) needs better wording. If 'name' does NOT have a unique 
non-nullable constraint declared on it - i.e. you can have several 'me's 
with identical 'name's - then DBIC can not rely on the results coming 
from the DBI cursor to be "grouped in slabs" representing every object 
hierarchy "hanging off" every individual 'me' instance. This is why it 
needs to exhaust the cursor to give you a "certifiably correct" answer.

> If it helps, Map::Body has 0 or 1 empires (can be null)

Your analysis is correct - the order on 'empire.id' ought to work, but 
this is extra (rather complicated) logic that currently is not 
implemented: note this TODO 
https://metacpan.org/source/RIBASUSHI/DBIx-Class-0.082820/t/prefetch/multiple_hasmany_torture.t#L131-132

> Any guidance on how to update this order_by to get DBIC to only fetch one row
> at a time would be appreciated.

Simply order by [ me.name, me.<primary_key> ] <--- this will ensure 
things are ordered in a *stable* manner on the leftmost side, and the 
collapse will proceed correctly. A PR/patch amending the warning text 
and/or the prefetch documentation to make this clearer will be *very* 
much appreciated.

> I'm expecting that once I roll this out into production and it gets wide use,
> we'll have a thousand rows ("Map::Body" objects) being returned, and up to 40
> or 50 empires, so I'd rather not resort to ->all to save some memory in the
> Plack processes.

Please note that depending on your DBD, DBI may *still* be loading the 
entire resultset into memory even if DBIC uses ->next on the surface. 
For example DBD::Pg does this invariably, and DBD::mysql does it by 
default unless you set https://metacpan.org/pod/DBD::mysql#mysql_use_result

Hope this is sufficient to get you going

Cheers!




More information about the DBIx-Class mailing list