So I got a mail from a client this morning saying that there was an error in the API I’m working on because it was returning duplicates. The client noticed that in the result set that was paginated over four pages, pages two and three had two duplicate IDs in them. This shouldn’t be possible since these IDs are in an auto-increment column in the database table.

This application is built with the Laravel based framework Lumen so I figured that it had to have something to do with the logic in the Laravel ORM, Eloquent. I thought that it might have been some bug that got fixed in more recent versions of the framework so I started googling for similar cases. I couldn’t find any information on similar issues so I figured that I would just extract the raw queries from the ORM and run them directly in MySQL so I fired up Sequel Pro and started querying the database.

I’m running MySQL 5.7.21 in Docker and haven’t tested other MySQL versions so I’m not sure if this is specific to this version. However, once I ran the queries in the database I found out that the problem was not in the Eloquent ORM but in MySQL itself. I was relieved that it was not the application but at the same time perplexed by not knowing how to fix this issue since I do not know the internals of MySQL.

The dataset that I was working with has a lot of created_at timestamps with the exact same date. MySQL returned the correct results when I was not using offset and limit to paginate the result. The same went for when I was sorting the data in ascending order and also when leaving out the order by clause in the query. The problem occurred when using offset and limit to paginate the result set and at the same time sorting it by created_at timestamp in descending order.

The following four queries were the ones that I executed to paginate the total 190 entries into four pages. Sure enough, the IDs 21990 and 21991 appeared on both page number 2 and 3.

select id,created_at from `products` where `status` <> 0 `created_at` desc limit 50 offset 0
select id,created_at from `products` where `status` <> 0 `created_at` desc limit 50 offset 50
select id,created_at from `products` where `status` <> 0 `created_at` desc limit 50 offset 100
select id,created_at from `products` where `status` <> 0 `created_at` desc limit 50 offset 150

I’m pretty sure this will not happen if you do not have many entries with the same value. That’s because if you are sorting on one column with many duplicates the order of them is not guaranteed if you are not specifying a secondary sorting column. So, in this case, the duplicate data was because of this specific dataset. That said, it is good practice to always sort by a second column as well, especially when working with pagination that is sorted on a column with many duplicates.

I sure learned my lesson and I hope that this was helpful for anyone out there in a similar situation.

I’ll finish the article by dropping the whole dataset here, have a good one!

select id,created_at from `products` where `status` <> 0 order by `created_at` desc
21914   2018-03-02 13:08:42
22077   2018-03-02 12:20:45
21975   2018-03-02 10:46:55
21786   2018-03-01 11:04:22
22050   2018-02-28 12:38:41
22051   2018-02-28 12:38:41
22052   2018-02-28 12:38:41
22053   2018-02-28 12:38:41
22054   2018-02-28 12:38:41
22055   2018-02-28 12:38:41
22056   2018-02-28 12:38:41
22057   2018-02-28 12:38:41
22058   2018-02-28 12:38:41
22059   2018-02-28 12:38:41
22060   2018-02-28 12:38:41
22061   2018-02-28 12:38:41
22062   2018-02-28 12:38:41
22063   2018-02-28 12:38:41
22064   2018-02-28 12:38:41
22065   2018-02-28 12:38:41
22066   2018-02-28 12:38:41
22067   2018-02-28 12:38:41
22068   2018-02-28 12:38:41
22069   2018-02-28 12:38:41
22070   2018-02-28 12:38:41
22071   2018-02-28 12:38:41
22072   2018-02-28 12:38:41
22073   2018-02-28 12:38:41
22074   2018-02-28 12:38:41
22075   2018-02-28 12:38:41
22076   2018-02-28 12:38:41
21995   2018-02-27 17:01:54
21998   2018-02-27 17:01:54
22000   2018-02-27 17:01:54
22001   2018-02-27 17:01:54
22003   2018-02-27 17:01:54
22005   2018-02-27 17:01:54
22007   2018-02-27 17:01:54
22009   2018-02-27 17:01:54
22010   2018-02-27 17:01:54
22011   2018-02-27 17:01:54
22013   2018-02-27 17:01:54
22014   2018-02-27 17:01:54
22015   2018-02-27 17:01:54
22018   2018-02-27 17:01:54
22019   2018-02-27 17:01:54
22020   2018-02-27 17:01:54
22021   2018-02-27 17:01:54
22022   2018-02-27 17:01:54
22023   2018-02-27 17:01:54
22024   2018-02-27 17:01:54
22025   2018-02-27 17:01:54
22026   2018-02-27 17:01:54
22028   2018-02-27 17:01:54
22030   2018-02-27 17:01:54
22033   2018-02-27 17:01:54
22036   2018-02-27 17:01:54
22038   2018-02-27 17:01:54
22040   2018-02-27 17:01:54
22041   2018-02-27 17:01:54
22043   2018-02-27 17:01:54
22045   2018-02-27 17:01:54
22047   2018-02-27 17:01:54
22048   2018-02-27 17:01:54
22049   2018-02-27 17:01:54
21869   2018-02-27 17:01:53
21870   2018-02-27 17:01:53
21872   2018-02-27 17:01:53
21873   2018-02-27 17:01:53
21874   2018-02-27 17:01:53
21876   2018-02-27 17:01:53
21877   2018-02-27 17:01:53
21878   2018-02-27 17:01:53
21879   2018-02-27 17:01:53
21880   2018-02-27 17:01:53
21881   2018-02-27 17:01:53
21882   2018-02-27 17:01:53
21884   2018-02-27 17:01:53
21885   2018-02-27 17:01:53
21886   2018-02-27 17:01:53
21887   2018-02-27 17:01:53
21888   2018-02-27 17:01:53
21889   2018-02-27 17:01:53
21890   2018-02-27 17:01:53
21893   2018-02-27 17:01:53
21894   2018-02-27 17:01:53
21895   2018-02-27 17:01:53
21896   2018-02-27 17:01:53
21898   2018-02-27 17:01:53
21899   2018-02-27 17:01:53
21901   2018-02-27 17:01:53
21904   2018-02-27 17:01:53
21906   2018-02-27 17:01:53
21909   2018-02-27 17:01:53
21910   2018-02-27 17:01:53
21911   2018-02-27 17:01:53
21912   2018-02-27 17:01:53
21915   2018-02-27 17:01:53
21916   2018-02-27 17:01:53
21917   2018-02-27 17:01:53
21918   2018-02-27 17:01:53
21919   2018-02-27 17:01:53
21920   2018-02-27 17:01:53
21921   2018-02-27 17:01:53
21922   2018-02-27 17:01:53
21923   2018-02-27 17:01:53
21924   2018-02-27 17:01:53
21925   2018-02-27 17:01:53
21927   2018-02-27 17:01:53
21929   2018-02-27 17:01:53
21931   2018-02-27 17:01:53
21933   2018-02-27 17:01:53
21934   2018-02-27 17:01:53
21936   2018-02-27 17:01:53
21938   2018-02-27 17:01:53
21940   2018-02-27 17:01:53
21942   2018-02-27 17:01:53
21944   2018-02-27 17:01:53
21945   2018-02-27 17:01:53
21946   2018-02-27 17:01:53
21947   2018-02-27 17:01:53
21948   2018-02-27 17:01:53
21951   2018-02-27 17:01:53
21953   2018-02-27 17:01:53
21956   2018-02-27 17:01:53
21958   2018-02-27 17:01:53
21960   2018-02-27 17:01:53
21961   2018-02-27 17:01:53
21963   2018-02-27 17:01:53
21965   2018-02-27 17:01:53
21967   2018-02-27 17:01:53
21968   2018-02-27 17:01:53
21969   2018-02-27 17:01:53
21970   2018-02-27 17:01:53
21972   2018-02-27 17:01:53
21977   2018-02-27 17:01:53
21978   2018-02-27 17:01:53
21980   2018-02-27 17:01:53
21981   2018-02-27 17:01:53
21982   2018-02-27 17:01:53
21983   2018-02-27 17:01:53
21985   2018-02-27 17:01:53
21987   2018-02-27 17:01:53
21988   2018-02-27 17:01:53
21989   2018-02-27 17:01:53
21990   2018-02-27 17:01:53
21991   2018-02-27 17:01:53
21992   2018-02-27 17:01:53
21855   2018-02-27 17:01:52
21857   2018-02-27 17:01:52
21859   2018-02-27 17:01:52
21861   2018-02-27 17:01:52
21864   2018-02-27 17:01:52
21867   2018-02-27 17:01:52
21780   2018-02-23 12:16:33
21772   2018-02-23 12:15:54
21812   2018-02-23 12:15:28
21802   2018-02-23 12:14:30
21733   2018-02-19 00:01:01
21735   2018-02-19 00:01:01
21736   2018-02-19 00:01:01
21737   2018-02-19 00:01:01
21738   2018-02-19 00:01:01
21739   2018-02-19 00:01:01
21741   2018-02-19 00:01:01
21742   2018-02-19 00:01:01
21743   2018-02-19 00:01:01
21746   2018-02-19 00:01:01
21747   2018-02-19 00:01:01
21748   2018-02-19 00:01:01
21749   2018-02-19 00:01:01
21751   2018-02-19 00:01:01
21752   2018-02-19 00:01:01
21753   2018-02-19 00:01:01
21754   2018-02-19 00:01:01
21755   2018-02-19 00:01:01
21756   2018-02-19 00:01:01
21757   2018-02-19 00:01:01
21759   2018-02-19 00:01:01
21761   2018-02-19 00:01:01
21763   2018-02-19 00:01:01
21766   2018-02-19 00:01:01
21767   2018-02-19 00:01:01
21503   2018-02-07 09:07:41
21547   2018-01-31 23:01:02
21548   2018-01-31 23:01:02
21281   2018-01-04 13:26:44
21240   2017-12-31 23:01:04
21250   2017-12-31 23:01:04
6   2006-02-04 14:50:49