-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Missing take and optimize some captures #347
Conversation
8276437
to
e94254b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self review with some food for thought.
src/Akka.Persistence.Sql/Query/Dao/BaseByteReadArrayJournalDao.cs
Outdated
Show resolved
Hide resolved
} | ||
).Via(_deserializeFlow); | ||
} | ||
|
||
|
||
internal async Task<List<JournalRow>> ExecuteEventQuery(QueryArgs queryArgs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentionally moved into it's own method,
Among other things, Another memory optimization for us to consider is -not- using Akka streams in the Unfold
internal read calls; Where this gets tricky, is that marshalling via Akka streams pipeline, neatly solves problems where a lot of DB Providers aren't really all that async 🙃.
SQLite (Fair enough,) I think some oracle providers, IDK about MySQL...
I can fold back in for now if needed, but it does make the pipeline at least a little bit neater.
src/Akka.Persistence.Sql/Query/Dao/BaseByteReadArrayJournalDao.cs
Outdated
Show resolved
Hide resolved
(jr, jtr) => (jr.Ordering == jtr.OrderingId), | ||
(jr, jtr) => jr) | ||
.OrderBy(r => r.Ordering) | ||
.Take(txInput.Max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, We didn't have a take here.
However, AFAIR, the LINQ query syntax tends to be more finicky with captures/closures than (carefully) plotted out L2Db Method syntax. However, if the lambda syntax somehow is producing something needed for a specific DB, we can go back to it. NVM It's not playing nice and we can deal with that later.
src/Akka.Persistence.Sql/Query/Dao/BaseByteReadArrayJournalDao.cs
Outdated
Show resolved
Hide resolved
src/Akka.Persistence.Sql/Query/Dao/BaseByteReadArrayJournalDao.cs
Outdated
Show resolved
Hide resolved
_cloneConnection = new Lazy<AkkaDataConnection>( | ||
() => new AkkaDataConnection( | ||
_opts.ConnectionOptions.ProviderName, | ||
new DataConnection(_opts))); | ||
opts.ConnectionOptions.ProviderName, | ||
new DataConnection(opts))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subtle thing; now we are capturing just the DataOptions
reference rather than the factory itself...
IDK how much the GC really cares but it sounds like the sort of thing that can annoy a cycle detector,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/Akka.Persistence.Sql/Query/Dao/BaseByteReadArrayJournalDao.cs
Outdated
Show resolved
Hide resolved
from lp in tagTable.Where(jtr => jtr.OrderingId == r.Ordering).DefaultIfEmpty() | ||
where lp.OrderingId > input.offset && | ||
lp.OrderingId <= input.maxOffset && | ||
from r in connection.GetTable<JournalRow>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would still be nice to convert this to method syntax later, if only because it's something like 300 lines of IL as it stands 😅
internal static Task<T> ExecuteWithTransactionAsync<TState,T>( | ||
this DbStateHolder factory, | ||
TState state, | ||
Func<AkkaDataConnection, CancellationToken, TState, Task<T>> handler) | ||
{ | ||
return factory.ConnectionFactory.ExecuteWithTransactionAsync(state, factory.IsolationLevel, factory.ShutdownToken, handler); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convenience method; since we are using the stateholder to avoid other captures, may as well add some sugar to go with it and make the main DAO cleaner.
|
||
internal static async Task<List<JournalRow>> ExecuteEventQuery(DbStateHolder stateHolder, TagMode tagMode, QueryArgs queryArgs) | ||
{ | ||
return tagMode != TagMode.TagTable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ugly and IDK how I feel but it's the easiest way to get more captures out of the way, short of some more significant refactoring where we had some different ways to compose without going back to Func<>
madness...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ugly
Use an if
statement instead of a ternary operator and it'll look better, I swear :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment that was somehow left unposted, I wound up moving these back into separate methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally LGTM, but did leave some comments
_cloneConnection = new Lazy<AkkaDataConnection>( | ||
() => new AkkaDataConnection( | ||
_opts.ConnectionOptions.ProviderName, | ||
new DataConnection(_opts))); | ||
opts.ConnectionOptions.ProviderName, | ||
new DataConnection(opts))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
async (connection, token) => | ||
return await input._dbStateHolder.ExecuteWithTransactionAsync( | ||
input.maxTake, | ||
async (connection, token,take) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just return a Task
here instead of async
/ await
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm honestly not sure, concerns aside from potential stack trace confusion on errors would be whether the fact this func is actually invoked inside two using
contexts... Better safe than sorry when it comes to that stuff IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some discussions on this are here:
- https://stackoverflow.com/questions/24110213/difference-between-returning-and-awaiting-a-task-in-an-async-method
- https://code-maze.com/charp-difference-between-returning-and-awaiting-a-task/
Would the performance boost for each query execution be worth it, does it outweight the possible exception stack trace confusion?
From my experience, the "proper" stack trace from an exception thrown from within an awaited Task is confusing in itself, since it is thrown from inside the async...await FSM and most often it will be a detached from the actual code that invoked it (when the thrown exception is caught by the debugger), so I can't really say that it is an actual improvement in debugging experience. But then again, I could be doing it wrong.
On the other hand, the article does point out that using proper async puts the exception where it belongs, in the future when it is supposed to happen, if it indeed needs to be thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to agree with @to11mtm on this one, returning a Task
from inside a using
block is quite dangerous because we don't know just how robust the IDispose
is being implemented by the 3rd party API developers.
|
||
internal static async Task<List<JournalRow>> ExecuteEventQuery(DbStateHolder stateHolder, TagMode tagMode, QueryArgs queryArgs) | ||
{ | ||
return tagMode != TagMode.TagTable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ugly
Use an if
statement instead of a ternary operator and it'll look better, I swear :p
@Aaronontheweb if you and @Arkatufus are OK with this feel free to merge, otherwise LMK what you would like to see changed. <3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
May help with #346
Changes
TAKE
on EventsByTag Query for Tag table case.ExecuteInTransactionAsync
that takes input state to minimize capturesChecklist
For significant changes, please ensure that the following have been completed (delete if not relevant):
Benchmarks
Will see what I can do...