How to cache wordpress get_posts query using transients?

pre_get_posts is used to modify the query arguments before the query is run. The $query is passed in by reference, so any changes you make in your function will be reflected in the query that’s actually run. The return that you’re doing will be ignored.

I think you might be making your problem worse here: you’re running the query, putting the results into a transient with an hour’s lifetime, then returning a value that will not be used. Then the query runs again once your action is done.