Challenge: Premature optimization, and all of that…
Let us look at the following pieces of code:
public void Consume(MyBooksRequest message)
{
var user = session.Get<User>(message.UserId);
bus.Reply(new MyBooksResponse
{
UserId = message.UserId,
Timestamp = DateTime.Now,
Books = user.CurrentlyReading.ToBookDtoArray()
});
}
public void Consume(MyQueueRequest message)
{
var user = session.Get<User>(message.UserId);
bus.Reply(new MyQueueResponse
{
UserId = message.UserId,
Timestamp = DateTime.Now,
Queue = user.Queue.ToBookDtoArray()
});
}
public void Consume(MyRecommendationsRequest message)
{
var user = session.Get<User>(message.UserId);
bus.Reply(new MyRecommendationsResponse
{
UserId = message.UserId,
Timestamp = DateTime.Now,
Recommendations = user.Recommendations.ToBookDtoArray()
});
}
Looking at this, I see that I have a requirement to getting my books, my queues and my recommendations. It appears that getting each datum is going to result in 2 queries, the first to load the User, and the second to lazy load the actual collection that we want to return.
An almost trivial optimization would be to eliminate the lazy loading, right? That would reduce the cost from 6 queries to just 3.
However, that assumption would be wrong. The following client code:
bus.Send(
new MyBooksRequest
{
UserId = userId
},
new MyQueueRequest
{
UserId = userId
},
new MyRecommendationsRequest
{
UserId = userId
});
Produces this SQL:
-- statement #1
enlisted session in distributed transaction with isolation level: Serializable
-- statement #2
SELECT user0_.Id as Id2_0_,
user0_.Name as Name2_0_,
user0_.Street as Street2_0_,
user0_.Country as Country2_0_,
user0_.City as City2_0_,
user0_.ZipCode as ZipCode2_0_,
user0_.HouseNumber as HouseNum7_2_0_
FROM Users user0_
WHERE user0_.Id = 1 /* @p0 */
-- statement #3
SELECT currentlyr0_.[User] as User1_1_,
currentlyr0_.Book as Book1_,
book1_.Id as Id0_0_,
book1_.Name as Name0_0_,
book1_.ImageUrl as ImageUrl0_0_,
book1_.Image as Image0_0_,
book1_.Author as Author0_0_
FROM UsersReadingBooks currentlyr0_
left outer join Books book1_
on currentlyr0_.Book = book1_.Id
WHERE currentlyr0_.[User] = 1 /* @p0 */
-- statement #4
SELECT queue0_.[User] as User1_1_,
queue0_.Book as Book1_,
queue0_.[Index] as Index3_1_,
book1_.Id as Id0_0_,
book1_.Name as Name0_0_,
book1_.ImageUrl as ImageUrl0_0_,
book1_.Image as Image0_0_,
book1_.Author as Author0_0_
FROM UsersWaitingBooks queue0_
left outer join Books book1_
on queue0_.Book = book1_.Id
WHERE queue0_.[User] = 1 /* @p0 */
-- statement #5
SELECT recommenda0_.[User] as User1_1_,
recommenda0_.Book as Book1_,
recommenda0_.[Index] as Index3_1_,
book1_.Id as Id0_0_,
book1_.Name as Name0_0_,
book1_.ImageUrl as ImageUrl0_0_,
book1_.Image as Image0_0_,
book1_.Author as Author0_0_
FROM UsersRecommendedBooks recommenda0_
left outer join Books book1_
on recommenda0_.Book = book1_.Id
WHERE recommenda0_.[User] = 1 /* @p0 */
-- statement #7
commit transaction
That seems strange, can you figure out why?
Bonus points for figuring out whatever it would be worth it to do the eager load optimization or not.