Skip to content
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

Querying when removing document can sometimes cause incorrect query result. #4088

Closed
amaghfur opened this issue Jul 28, 2015 · 4 comments
Closed

Comments

@amaghfur
Copy link

If a query occur during a routine that removes a document, the query can result in strange results. Run the code below anywhere with PouchDB. If you adjust the setTimeout delay so that 'end remove' happens in the middle of the query (after 'start query' and before 'end query'), the query result is weird.

I encountered this when designing a db whose internal logic does occasional document removal while still serving external users with querying functionality. So quite often the remove can happen mid-query.

var db = window.__testDb = new PouchDB('test')
db.destroy().then(function(){
    var ddoc = {
        _id: '_design/type',
        views: {
            type: {
                map: function mapFun(doc) {
                    if (doc.type) {
                        emit(doc.type);
                    }
                }.toString()
            }
        }
    }

    var doc1 = {
        _id: '001',
        item: 'Good Kill',
        type: 'Movie',
        pts: '2015-07-21T07:00:00.000Z'
    }
    var doc2 = {
        _id: '002',
        item: 'The Dark Knight',
        type: 'Movie',
        pts: '2015-07-21T08:00:00.000Z'
    }

    var revToRemove;

    db = new PouchDB('test')
    db.groupBy = function(type){
        return new Promise(function(resolve, reject){
            var ddocName = "groupby_" + type + "_sortedby_lastupdated"

            var mapFunction = "function(doc){ if (doc.type === '" + type + "') { emit(doc.pts) } }"
            var ddoc = {
                _id: '_design/' + ddocName,
                views: {}
            };
            ddoc.views[ddocName] = {
                map: mapFunction.toString()
            };

            db.put(ddoc).then(function(){
                resolve(ddocName)
            }).catch(function(){
                // design doc already exist
                resolve(ddocName)
            })       
        })
    }

    db.put(doc1).then(function(res){
        revToRemove = res.rev
        return db.put(ddoc)
    }).then(function(){
        return db.groupBy('Movie');
    }).then(function(ddocName){
        console.time('put')
        console.time('query')
        setTimeout(function(){
            console.log('start put')
            db.put(doc2).then(function(){
                console.log('end put')
                console.log('start remove')
                db.remove(doc1._id, revToRemove).then(function(){
                    console.log('end remove')
                    console.timeEnd('put')
                })
            })          
        }, 30) // problem extremely likely to happen if timeout set between 20-50ms ('put' time and 'query' time within 20ms of each other)
        console.log('start query')
        return db.query(ddocName, {
            startkey: '2015-07-21T09:00:00.000Z',
            include_docs: true,
            limit: 5,
            descending: true
        })
    }).then(function(res){
        console.log('end query')
        console.timeEnd('query')
        console.log('res:', res)
        console.table(res.rows)
    }).catch(function(err){
        console.log('err:', err)
    })
})
@nolanlawson
Copy link
Member

Yeah, there is a race condition when using include_docs:true with query(). In fact, CouchDB has this same problem. The only solution, sadly, is to emit() your documents and avoid using include_docs:true.

@nolanlawson
Copy link
Member

I'll note that if we manage to write native secondary indexes as a single transaction (#3775), then we could avoid the race condition.

@amaghfur
Copy link
Author

Your suggestion to emit the doc instead of using include_docs achieved my desired result. Thanks!

@NickColley
Copy link
Contributor

Awesome! Feel free to re-open if you've got any more questions! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants