-
-
Notifications
You must be signed in to change notification settings - Fork 1.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
Eager loading of sibling relation causes fatal error #3065
Comments
maybe need use .with(.$field.$pivots) |
We tested it, but unfortunately the error persists. |
What does your code look like that actually triggers the error (where you're accessing the properties) |
The error comes from the query, as I said, when loading the sibling relation. The code for this is basically this: func getAllHandler( The entity and the migration look as previously described.. |
In the code snippet you're returning a DTO - are you sure it's not happening there? |
yes, I'm sure. The error is thrown before the dto is created and then of course when I remove the dto completely from the code. I think it fails in the call to the all() function in the query. At least that's the last point I can reasonably track with the debugger. |
Do you have any idea where else this could be coming from, or can I provide any other information to look for the problem? Sorry to ask again...but we are kind of stuck here |
@karola-gr I've been unable to reproduce this. Do you have a reproducible project you can share? |
@karola-gr does it give fatalError if you don't select specific fields from the table? |
@Kishimotovn No, this only happens with the specific sibling relations, not with other fields. as soon as I exclude the siblings from the query and reload them directly afterwards, it works without problems. Maybe there is some problem with the combination with the optionals that something is assumed to be nil... But yes...reload the relation is also our current workaround. @0xTim , it is difficult to recreate the scenario in an minimal example project. I think the only way would be to look at our project remotely in a call or something. |
Hi :) I have set up a small gitlab project in which I have rebuilt the part of the original project that fails. Maybe this will help. @0xTim ...I have invited you to the repo. |
Hello, |
@karola-gr I've just tried running the tests from the GH repo you uploaded and I don't get any crashes. It retrieves items from the DB and returns them and doesn't fail like you are seeing. I get
Are you running the latest packages etc? |
Yes, I updated all the packages again, but still got this:
Well, this confuses me even more now :D |
The best thing would be do a clean repo clone and follow the steps. If that fails then you must have a code difference. You can see the line in the stack trace that causes the issue |
So I tried a few things again today. I used the postgres and the redis. I deleted derived data, did a clean build, recloned the repo in exactly the same state, reloaded all packages... I always did: In a few cases, which were not reproducible for me, it worked. The first time was when I switched to linux..then I tried to build the original project on linux and that did not work. The test repo didn't work after that either...it went on like that all day. It feels really random... :( The stack trace doesn't really get me anywhere either, to be honest. It breaks between the .all() and the dto |
Oh I think I know what's going on. The error message is actually subtly different and it's nothing to do with sibling relations. The error is about specific fields and not relations - I'm guessing you have a model field that has a null column in the database that should be |
i created the data in such a way that there are two entries that are null in one column each (exerciseBaseId in the first entry andexerciseStandardId in the other). However, these two columns are declared as optionalParents. Even if I replace all null entries with data, I still get the error. Reloading these fields also works without any problems. But it does not work directly in the query. I am trying to access its siblings in the query via an optional parent. Maybe it searches for null in the sibling relation when the optional parent is empty and crashes at this point? |
I removed the optional parent again and only looked at the sibling relation. Here I get a nil when it crashes, but there shouldn't actually be a nil. It is required and I don't see any entry in the database that has a nil in this column. |
I have double checked the data on the database as well as the db dump and it seems all correct. Also a nil value in a required column should already lead to a problem when saving to the database or lazy load them, right? |
@karola-gr, very likely unrelated, but |
We have encountered the exact same problem. Whenever we try to eager load sibling relationships using |
Oh, that's good to read that we're not the only ones with this problem. Do you have any idea what the problem could be? |
@karola-gr sorry for the delayed reply. Unfortunately no. I cannot eager load any sibling relationship. It doesn't fail sporadically, it fails consistently, and I can't find a workaround. |
Thanks for the answer anyway. Are there any updates on this? We have now restructured our database so that we have removed all optional parents, but this does not change the error. As @theoks says, it fails consistently when preloading siblings of an entity. The only thing that works for us is to load the siblings separately after fetching the entity. |
Is it possible to show actual code - or at least code which compiles - which exhibits this issue? The pseudocode in the original post is both incomplete and invalid in ways that make it difficult to see where things might be going wrong. |
Unfortunately, I cannot provide the actual code. I tried to give an extract here: |
In the attached code I do not see the definition of |
oh sorry. This is the corresponding model |
I have so far been unable to reproduce the issue with the code you provided. I added the following additional code to put it together into a buildable project: struct CreateBaseTables: AsyncMigration {
func prepare(on database: any Database) async throws {
try await database.schema(ExerciseDefinition.schema).id().create()
try await database.schema(ExerciseVariation.schema).id().create()
}
func revert(on database: any Database) async throws {
try await database.schema(ExerciseVariation.schema).delete()
try await database.schema(ExerciseDefinition.schema).delete()
}
}
struct AddTestData: AsyncMigration {
func prepare(on database: any Database) async throws {
// create a definition
let def = ExerciseDefinition()
try await def.create(on: database)
// create a couple of variations
let var1 = ExerciseVariation(), var2 = ExerciseVariation()
try await [var1, var2].create(on: database)
// attach the variations to the definition through pivots
try await def.$attachedExerciseVariations.attach([var1, var2], on: database)
// create an exercise using the definition
let exc = Exercise(exerciseDefinitionID: try def.requireID())
try await exc.create(on: database)
}
func revert(on database: any Database) async throws {}
} I also had to comment out the authentication logic, the additional eager loading, and the DTO in the controller (as those types are not present in the provided code), and I converted the logic to be fully Concurrency-aware, with this result: struct ExerciseController: RouteCollection {
func boot(routes: any RoutesBuilder) throws {
let routes = routes.grouped("api", .constant(Exercise.schema))
routes.get(":ExerciseID", use: self.getHandler)
}
@Sendable func getHandler(_ req: Request) async throws -> Exercise {
guard let exerciseIDString = req.parameters.get("ExerciseID"),
let exerciseID = UUID(uuidString: exerciseIDString)
else {
throw Abort(.badRequest, reason: "Missing parameters in request")
}
guard let exercise = try await Exercise
.query(on: req.db)
.filter(\.$id == exerciseID)
.with(\.$exerciseDefinition, { $0.with(\.$attachedExerciseVariations) })
.first()
else {
throw Abort(.notFound)
}
return exercise
}
} I added these and the other code you provided to a default Vapor template project, and was unable to reproduce the crash. As an aside, I ended up with some minor style and best practices notes with respect to the provided code, hopefully they are helpful in general despite being unrelated to this issue:
|
We have encountered the same problem again in another relation. This time a simple parent relation. So could it be that the length of the key name is a problem? In the other case, the key was also quite long.
|
Ah yes! There is a known issue with over-length field names combined with long table names where the aliased identifier generated by Fluent in the query ends up truncated by the database, resulting in Fluent not loading the field properly. At the moment the only workaround is the one you've discovered - shortening the key 😕. |
I strongly doubt I'll end up solving this in Fluent 4 at this point; the best I can offer is that Fluent 5 won't have this issue. |
Description
Eager loading of sibling relation causes fatal error.
Fatal error: Cannot access field before it is initialized or fetched
The data in the database seems to be correctly created. The relations exist and are filled. However, I cannot access them in a query. The problem only occurs with some sibling relations, but only when querying a sibling that is held by an optional parent, or with a sibling that may be empty. Not with all of them.
Details
Case 1:
Sibling relation of optional parent
Example EntityOne with sibling relation to EntityTwo
EntityThree with optional parent relation to EntityOne
--> Query
BUT:
entity.entityThree.load(on: db)
works fineCase 2:
Same behaviour as Case 1, but without optional parent relation
Example EntityOne with sibling relation to EntityTwo
RelationTable (same for case 1)
Eager loading in query failes, but loading afterwards works fine.
But:
try await entityOne.$allowedEntityTwo.loadIfNeeded(on: db)
also works fine.Migration for relation table
Expected behavior
Sibling relations should be eager loaded / accessed in query
Environment
Vapor 4.77
MacOS: 13.5
The text was updated successfully, but these errors were encountered: