-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
GraphQL session MARS/DataReader is open problem #9330
Comments
Hi you need to use ‘ResolveLockedAsync’ instead of ResolveAsync currently
when making db calls in resolvers. We’re hoping to change this behaviour
with the graphqldotnet 4 upgrade we’re working on currently.
…On Fri, 30 Apr 2021 at 18:05, tropcicstefan ***@***.***> wrote:
Describe the bug
Resolving graphQLFields of content part through DataLoader causes MARS or
Datareader is open exception.
To Reproduce
Latest dev branch
Make content Part with two taxonomy fields(JournalistCategory,
ArticleCategory). Make custom GraphQLObjectType that exposes those data as
two Field<ListGraphType<ContentItemInterface>, ContentItem[]> and resolve
those fields querying DB for linked taxonomy and parsing it to get that
specific ContentItem. It appears as having two or more graphQLFields when
resolving through ISession is too much
`
public class CustomCategoryObjectType : ObjectGraphType<CustomCategoryPart>
{
public CustomCategoryObjectType(IStringLocalizer<CustomCategoryObjectType> S)
{
Name = "CustomCategory";
Description = S["Alternative part definition"];
Field<ListGraphType<ContentItemInterface>, ContentItem[]>()
.Name("journalistMetadata")
.Description("Exposed journalist taxonomy metadata")
.PagingArguments()
.ResolveAsync(context =>
{
var journalist = context.Source.JournalistCategory.TermContentItemIds.ElementAtOrDefault(0);
if (journalist == null)
{
return null;
}
var serviceProvider = context.ResolveServiceProvider();
var accessor = serviceProvider.GetRequiredService<IDataLoaderContextAccessor>();
var session = serviceProvider.GetService<ISession>();
var contentItemLoader = accessor.Context.GetOrAddBatchLoader<string, ContentItem>("GetJournalistsById",
ci => LoadTaxContentItems(ci, session, Const.Taxonomy.Journalist));
return contentItemLoader.LoadAsync(context.Page(context.Source.JournalistCategory.TermContentItemIds));
});
Field<ListGraphType<ContentItemInterface>, ContentItem[]>()
.Name("categoryMetadata")
.Description("Exposed category taxonomy metadata")
.PagingArguments()
.ResolveAsync(context =>
{
var category = context.Source.ArticleCategory.TermContentItemIds.ElementAtOrDefault(0);
if (category == null)
{
return null;
}
var serviceProvider = context.ResolveServiceProvider();
var accessor = serviceProvider.GetRequiredService<IDataLoaderContextAccessor>();
var session = serviceProvider.GetService<ISession>();
var contentItemLoader = accessor.Context.GetOrAddBatchLoader<string, ContentItem>("GetCategoryById",
ci => LoadTaxContentItems(ci, session, Const.Taxonomy.Category));
return contentItemLoader.LoadAsync(context.Page(context.Source.ArticleCategory.TermContentItemIds));
});
}
private async Task<IDictionary<string, ContentItem>> LoadTaxContentItems(
IEnumerable<string> contentItemIds,
ISession session,
string taxItemId)
{
if (contentItemIds is null || !contentItemIds.Any())
{
return new Dictionary<string, ContentItem>();
}
var tax = await session
.Query<ContentItem, ContentItemIndex>(y =>
y.ContentItemId == taxItemId
&& y.Published)
.FirstOrDefaultAsync();
if (tax is null)
{
return new Dictionary<string, ContentItem>();
}
var contentItemsLoaded = tax.As<TaxonomyPart>().Terms.Where(x => contentItemIds.Contains(x.ContentItemId)).ToList();
return contentItemsLoaded.ToDictionary(k => k.ContentItemId, v => v);
}
}
`
Expected behavior
@carlwoodhouse <https://github.com/carlwoodhouse> here #4844
<#4844> says that
Dataloaders should be enough to not cause this issue
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9330>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADRUEEUCLI2Z3EYU34EJK3TLLPOBANCNFSM434ZEVRQ>
.
|
But |
it should be able too, all it does is force a lock to stop parallel db calls (so eliminating the mars issue) - however looking through the code as you say the list parts should use it. Honestly its a long time since i looked at it so memory is a bit hazy .... and im away atm so cant look properly on phone. but can investigate when im back. (again it will be fixed anyway by the graphqldotnet uplift we're working on which has much better first class di support) |
Changing ResolveAsync to ResolveLockedAsync with DataLoaders appears to cause a deadlock because request just "hangs". ...looking forward for upgrade |
Ah ok that actually makes sense ..
In the mean time You could try resolving the session in the method your
passing to the data loader instead of passing the session (reverting to
loadasync); not sure it will work but those you run sync after all the
fields have resolved in parallel which is what’s causing the issue
…On Fri, 30 Apr 2021 at 22:01, tropcicstefan ***@***.***> wrote:
it should be able too, all it does is force a lock to stop parallel db
calls (so eliminating the mars issue)
Changing ResolveAsync to ResolveLockedAsync with DataLoaders appears to
cause a deadlock because request just "hangs".
...looking forward for upgrade
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9330 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADRUEEUAAALVR3ISJI6XTLTLMLBZANCNFSM434ZEVRQ>
.
|
I tried passing context or service provider and in both cases error is still occurring.
|
Try to lock on the session object by yourself, and also use |
@tropcicstefan Is this issue still relevant? |
So if this issue is still active and cant be fixed by adding LockedAsyncFieldResolver, won't we finish the PR for GQL4 #9087 which should address this? Big problem is, that this issue can occur unexpectedly - it happens only in combination with some field which is using dataloader (e.g. TaxonomyField), so it may not be found in development of custom field and then it is complicated to find root cause of error, because error usually does not happen in your custom field, but points to TaxonomyField.. how should you find it when you have several custom fields.. I would love to help finish that, but I lack enough information about dataloaders etc. My primary focus is to use OC as headless and I feel that this should be resolved, not sure if most people use OC as monolith instead of headless? |
Feel free to open a Pull Request that targets ag/graphql4 branch. |
I did some work on that PR too, but I am stuck, because there are some problems with dataloaders (n+1 issue, this problem..) that I do not have information about (how to test them e.g.) and seems like nobody will provide info - I do not know how to fix something I have no info about. Is there a way to contact someone who knows more about it directly, so I could potentially fix it? |
@MikeKry Regarding this issue: So way to resolve this issue is either by: ...would like to have another pair of eyes on my take |
I actually think with the changes in gql dotnet we can remove the locks ..
and use the serial data execution strat we used too but removed because it
didn’t support dataloaders .. which would fix these issues .. supposedly
the dataloaders now supports it.
Unfortunately I don’t really have the time to look into it myself at the
moment ..
…On Mon, 21 Mar 2022 at 15:40, tropcicstefan ***@***.***> wrote:
@MikeKry <https://github.com/MikeKry>
If you are referencing @carlwoodhouse <https://github.com/carlwoodhouse>
about n+1 problem in your pr
<#9087>, I think he just
said that dataloaders should be tested. You can test dataloaders with
content item(articles) with content picker field(linked articles) and
startup SQL Server profiler. In there you should see only one query
regarding content picker field. So if you query for 10 articles there
should be 1 query for linked articles with long where statement, and not 10
queries with short where :D
Regarding this issue:
My assumption was I need ResolveLockedAsync when using ISession. Truth is
it depends, when you want to resolve field by field(introducing n+1),
without dataloader, you should use AsyncResolver, but with dataloaders you
should use ResolveAsync and lock inside dataloader, in delegate when you
use ISession.
So way to resolve this issue is either by:
a) updating docs and saying hey when writing custom graphql fields with
dataloader lock on ISession, update current uses of dataloader i.e.
ContentPickerField with locking.
b) write framework implementation of IDataLoader binded to YesSQL with
lock on ISession
—
Reply to this email directly, view it on GitHub
<#9330 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADRUEHOXT27XNLX3CA2KVTVBCKADANCNFSM434ZEVRQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
^^ the patch notes of the new version suggest so anyway
On Mon, 21 Mar 2022 at 17:14, Carl Woodhouse ***@***.***>
wrote:
… I actually think with the changes in gql dotnet we can remove the locks ..
and use the serial data execution strat we used too but removed because it
didn’t support dataloaders .. which would fix these issues .. supposedly
the dataloaders now supports it.
Unfortunately I don’t really have the time to look into it myself at the
moment ..
On Mon, 21 Mar 2022 at 15:40, tropcicstefan ***@***.***>
wrote:
> @MikeKry <https://github.com/MikeKry>
> If you are referencing @carlwoodhouse <https://github.com/carlwoodhouse>
> about n+1 problem in your pr
> <#9087>, I think he just
> said that dataloaders should be tested. You can test dataloaders with
> content item(articles) with content picker field(linked articles) and
> startup SQL Server profiler. In there you should see only one query
> regarding content picker field. So if you query for 10 articles there
> should be 1 query for linked articles with long where statement, and not 10
> queries with short where :D
>
> Regarding this issue:
> My assumption was I need ResolveLockedAsync when using ISession. Truth is
> it depends, when you want to resolve field by field(introducing n+1),
> without dataloader, you should use AsyncResolver, but with dataloaders you
> should use ResolveAsync and lock inside dataloader, in delegate when you
> use ISession.
>
> So way to resolve this issue is either by:
> a) updating docs and saying hey when writing custom graphql fields with
> dataloader lock on ISession, update current uses of dataloader i.e.
> ContentPickerField with locking.
> b) write framework implementation of IDataLoader binded to YesSQL with
> lock on ISession
>
> —
> Reply to this email directly, view it on GitHub
> <#9330 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AADRUEHOXT27XNLX3CA2KVTVBCKADANCNFSM434ZEVRQ>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
|
Are data loaders invoked in parallel threads? That would explain the issue. If it's configurable it should be "serial". This is a common issue we had years ago with the defaults in GrapQL.NET and we explicitly use serial mode for some settings since then. |
Moving to backlog since there is some work going on with upgrading the library. |
I looked into this during analysis while doing changes in GQL4 update I think it might be also caused by cache - dataloaders by default return cached result for same key, so it is possible that it is being used by multiple functions. There could be two solutions If you agree, I can try one (or both) of them in #11499 |
I ran into another issue, which seems that it might not be related directly to dataloaders.. Happens only sometimes on production env. maybe when there are multiple requests at same time. It is again related to .As<> (could there be some bug? I am running on OC 1.3), but no dataloaders involved inside LayerQueryObjectType. There are content pickers inside widgets, so it may be same issue, just hidden under this error message.
|
Yes if shared objects are mutated in concurrent requests they would need to be thread safe or would need e.g. to retrieve clones from the cache, but still possible that it happens in the same request scope if there are non awaited tasks, in that case scope objects can be still concurrently mutated. As mentioned in other issues, most of the time we have MARS issues it is due to non awaited tasks. Related to GraphQl just saw the following using a OrchardCore/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Extensions/DataLoaderExtensions.cs Lines 10 to 20 in 2c2696d
The above being used by Maybe related |
Hmm, maybe a good chance that the usage of I think it is worth to try, I may suggest a PR when I will have time |
yes, for now I have seen it only in queries that contain (more than one) ContentPickerFields (or custom fields with same dataloader), so it might be this. |
That is not a solution because how dataloaders work and that method isn't called anywhere. The solution for this issue can be resolved in graphql version update branch with SerialDocumentExecuter like this
|
Yes the method I changed isn't called anywhere, this is the one defined just before that uses a In fact I updated the wrong method, will update it tomorrow. |
@tropcicstefan Okay I better understand now, thanks. @tropcicstefan @MikeKry So I will revert #11536, see my comment #11548 (comment), also about a possible solution. |
I may try serialexecution as @tropicstefan suggested in update PR I created few days ago #11499, if nobody is already working on that. But I wont get to that until next friday.
… 16. 4. 2022 v 5:07, Jean-Thierry Kéchichian ***@***.***>:
@tropcicstefan Okay I better understand now, thanks.
@tropcicstefan @MikeKry So I will revert #11536, see my comment #11548 (comment), also about a possible solution.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.
|
Describe the bug
Resolving graphQLFields of content part through DataLoader causes MARS or Datareader is open exception.
To Reproduce
Latest dev branch
Make content Part with two taxonomy fields(JournalistCategory, ArticleCategory). Make custom GraphQLObjectType that exposes those data as two
Field<ListGraphType<ContentItemInterface>, ContentItem[]>
and resolve those fields querying DB for linked taxonomy and parsing it to get that specific ContentItem. It appears as having two or more graphQLFields when resolving through ISession is too much`
`
...call GraphQL with both fields in query
query MyQuery { article(first: 10) { customCategory { categoryMetadata { displayText } journalistMetadata { displayText } } } }
Expected behavior
#4844 if we should use locking resolver why aren't content picker field and list part using it. probably type with list part and cpf can cause the same exception
Splitting fields in separate parts makes this issue occur less frequently
The text was updated successfully, but these errors were encountered: