objectbox / objectbox-go

Embedded Go Database, the fast alternative to SQLite, gorm, etc.
https://objectbox.io
Apache License 2.0
1.1k stars 45 forks source link

Objectbox is "hanging dead" when trying to delete "parent" table (relation one-to-many) #25

Closed alecpetrosky closed 4 years ago

alecpetrosky commented 4 years ago
type Medium struct {
    Id         uint64
    Collection *Collection `objectbox:"link"`
}

type Collection struct {
    Id          uint64
}

If in both boxes data present, calling

collectionBox := model.BoxForCollection(ob)
collectionBox.RemoveAll()

before

mediumBox := model.BoxForMedium(ob)
mediumBox.RemoveAll()

and Objectbox just does something unknown indefinitely until interrupted. No errors, nothing.

alecpetrosky commented 4 years ago

Removing all elements from mediumBox before emptying collectionBox, works just fine. So I guest it's the question of cascade delete handling logic.

greenrobot commented 4 years ago

It makes sense to remove Medium before Collection because the relation data is part of Medium. Thus you delete both things in one go. If you delete the Collection first, it will clear out the relation in all Medium objects additionally. This step is probably what takes most time. It's time wasted as you remove all Medium objects afterwards anyways. We should point that out in the docs, I guess.

However, I see quite a gap between "waiting indefinitely" and "works fine". Thus I'd like to investigate.

  1. How many objects for each type do you have stored at that point?

  2. Do you have some timing data (how long do those removes take)?

  3. Do you happen to have some code to reproduce this?

Thanks!

vaind commented 4 years ago

Couldn't reproduce yet, e.g. with test entities from the repo:

func TestRelationsRemoval(t *testing.T) {
    var env = model.NewTestEnv(t)
    defer env.Close()

    var total = uint64(10000)

    var relBox = model.BoxForTestEntityRelated(env.ObjectBox)
    var relObject = model.TestEntityRelated{}
    relBox.Put(&relObject)

    for i := uint64(0); i < total; i++ {
        id, err := env.Box.Put(&model.Entity{
            Related: relObject,
        })

        assert.NoErr(t, err)
        assert.True(t, id == i+1)
    }

    count, err := env.Box.Count()
    assert.NoErr(t, err)
    assert.Eq(t, total, count)

    count, err = relBox.Count()
    assert.NoErr(t, err)
    assert.Eq(t, uint64(1), count)

    assert.NoErr(t, relBox.RemoveAll())
    empty, err := relBox.IsEmpty()
    assert.NoErr(t, err)
    assert.True(t, empty)

    count, err = env.Box.Count()
    assert.NoErr(t, err)
    assert.Eq(t, total, count)

    assert.NoErr(t, env.Box.RemoveAll())
    empty, err = env.Box.IsEmpty()
    assert.NoErr(t, err)
    assert.True(t, empty)
}

@alecpetrosky could you please post a minimum reproducible example?

greenrobot commented 4 years ago

@vaind maybe try with 1 M objects?

vaind commented 4 years ago

Hmm, interestingly the issue starts at 10k objects (exactly)... var count = uint64(9999) finishes in 200ms, while 10000 goes on "forever" (i stopped it after a couple of seconds).

alecpetrosky commented 4 years ago

Hmm, interestingly the issue starts at 10k objects (exactly)... var count = uint64(9999) finishes in 200ms, while 10000 goes on "forever" (i stopped it after a couple of seconds).

My case was 100k objects. I cancelled it after 5 hours.

alecpetrosky commented 4 years ago
1. How many objects for each type do you have stored at that point?

Collection: 2 objects Medium: 100k

2. Do you have some timing data (how long do those removes take)?

RemoveAll Medium, then RemoveAll Collection ~ 1 second, no more. RemoveAll Collection, then RemoveAll Medium - never happens.

vaind commented 4 years ago

Thanks, I've been able to reproduce the issue and the fix is WIP and will come with the next C-API release. Will update this issue once Go is updated to use that c-api version.

In the meantime, one of the workarounds is to remove relation source entities ("Medium" in your example) in smaller batches (less than 10k at once).

greenrobot commented 4 years ago

Fixed internally. However, not sure when we release this as is located in the C API, which progressed quite a bit (ObjectBox Go has to catch up first). @alecpetrosky Are you good with the "workarounds" for a while? As I've written before, removing Medium before Collection makes more sense anyway in terms of performance.

alecpetrosky commented 4 years ago

@greenrobot yes, I'm good with this workaround. The most important thing is that you fixed it so quickly and in some time in the future it'll land in Go. Thank you. Great job!

vaind commented 4 years ago

released in https://github.com/objectbox/objectbox-go/releases/tag/v1.2.0