1) If we return the same PreparedStatement instance, there might be unexpected/undefined behaviour when 2 threads modify the value of a ps and execute a bound statement with it.
A solution can be to return a different wrapper each time.
If we don't introduce a wrapper, this might be a breaking change to the users that we must document (we could introduce it on a major version).
2) Calling Prepare() will be a cheap operation (in terms of processing costs) most of the time, except when the query needs to be prepared on the server. On one side, getting an item from a cache can be expected to be a sync operation but on the other as it might block, we should recommend the async method over the sync method.
3) By delaying query preparation, users might experience a spike in latencies the first time a query is accessed.
1) Introduce it in the next major, use a single ps instance per query, recommend users to set the parameters that might change at BoundStatement level.
2) Recommend in the docs to use the async method when using the cache to avoid blocking the calling thread.
3) Not a concern for us, users can still prepare it before making their app/service available.