morphia icon indicating copy to clipboard operation
morphia copied to clipboard

Regression when querying objects with cyclic references

Open DerNamenlose opened this issue 3 years ago • 4 comments

Describe the bug When you have entites with cyclic references in your model, querying them results in an exception with a huge stack trace. This seems to be due to an endless recursion when trying to resolve the reference.

To Reproduce Steps to reproduce the behavior:

  1. Create a model with two entities referencing each other (e.g. a project referencing issues with the issues having a back reference to their project). See attached files for an example.
  2. Insert two objects with cyclic references.
  3. Query one of the objects and observe the exception in the attached log file.

Expected behavior Successfully resolve the references like in the equivalent code in 1.6.1.

Please complete the following information:

  • Server Version: 5.0.2
  • Driver Version: 4.2.2
  • Morphia Version: 2.2.2

Additional context stacktrace.log

App.java

package test;

import com.mongodb.client.MongoClients;

import org.bson.types.ObjectId;

import dev.morphia.Morphia;

public class App {
    public static void main(String[] args) {
        var mongoClient = MongoClients.create("...");

        var ds = Morphia.createDatastore(mongoClient, "cyclic-test");
        ds.getMapper().map(E1.class, E2.class);

        var e1 = new E1(new ObjectId(), "Stuff");
        var e2 = new E2(new ObjectId(), e1);
        e1.setRelated(e2);

        ds.save(e1);
        ds.save(e2);

        var ds2 = Morphia.createDatastore(mongoClient, "reference-test");
        var e1returned = ds2.find(E1.class).iterator().next();

        System.out.println("returned " + e1returned.getName() + ", " + e1returned.getRelated());
    }
}

E1.java

package test;

import org.bson.types.ObjectId;

import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Reference;

@Entity
public class E1 {

    public E1() {
    }

    public E1(ObjectId id, String name) {
        this.setId(id);
        this.setName(name);
    }

    public ObjectId getId() {
        return id;
    }

    public void setId(ObjectId id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public E2 getRelated() {
        return related;
    }

    public void setRelated(E2 related) {
        this.related = related;
    }

    @Id
    private ObjectId id;
    private String name;

    @Reference(idOnly = true, ignoreMissing = true)
    private E2 related;
}

E2.java

package test;

import org.bson.types.ObjectId;

import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Reference;

@Entity
public class E2 {

    public E2() {

    }

    public E2(ObjectId id, E1 related) {
        this.setId(id);
        this.setRelated(related);
    }

    public E1 getRelated() {
        return related;
    }

    public void setRelated(E1 related) {
        this.related = related;
    }

    public ObjectId getId() {
        return id;
    }

    public void setId(ObjectId id) {
        this.id = id;
    }

    @Id
    private ObjectId id;

    @Reference(idOnly = true, ignoreMissing = true)
    private E1 related;
}

DerNamenlose avatar Nov 12 '21 14:11 DerNamenlose

reference caching was dropped in 2.x because of how persistence works now. What's needed are certain hooks down in the driver that aren't currently available. I have a patch in mind to submit but I've just not gotten it submitted just yet. I'll try to prioritize that. For now, lazy references are the fix here and even that will end up with two different instances so you'll need to be mindful of that.

evanchooly avatar Nov 12 '21 17:11 evanchooly

Hm, good to know. That is a bit of a blocker for us then. I'll have to look into the data model on how often we actually use cyclic dependencies. Thx for the heads up.

DerNamenlose avatar Nov 15 '21 06:11 DerNamenlose

fwiw, https://github.com/mongodb/mongo-java-driver/pull/846

evanchooly avatar Dec 26 '21 02:12 evanchooly

this probably won't make 2.3 but I don't want to lose track of the idea I have in mind that might resolve this. Will probably get bumped to 3.0 (or 2.4 whichever comes next) as I think even my grand idea will need slight tweaks to the driver.

evanchooly avatar Jul 09 '22 03:07 evanchooly