Local mapreduce cache not cleared for old revisions of existing views
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 45/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- javascript
- Lĩnh vực
- databases
Hướng nghiên cứu
Bắt đầu với packages/node_modules/pouchdb-abstract-mapreduce/src/createView.js và index.js, sau đó chạy case đã sửa trong tests/mapreduce/test.persisted.js. Theo dõi cách view signatures, _local/mrviews entries, cached views và viewCleanup tương tác qua các revision. Hoàn thành khi test ghi nhận việc dọn dẹp các cache của revision đã lỗi thời mà không xóa nhầm một cache vẫn được view khác sử dụng.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Issue
I have noted some surprising behavior around the mapreduce query cache for a local persistent view. Perhaps I am missing something, but it appears that each time a view is updated (e.g by PUTting a new revision of the design doc with a different map/reduce function for the view), a new cache entry will be created for the new view revision. However, the previous revision is never cleared from the cache, even when viewCleanup is triggered. These revision caches will continue to accumulate and I cannot see how they would ever get removed except by actually removing the view index, itself.
Reproduce
I have modified an existing mapreduce test to illustrate this behavior. This test passes, but hopefully you can tell from the code/comments that the behavior seems problematic.
it('Returns ok for viewCleanup after modifying view', async () => {
const db = new PouchDB(dbName);
const ddoc = {
_id: '_design/myview',
views: {
myview: {
map: function (doc) {
emit(doc.firstName);
}.toString()
}
}
};
const doc = {
_id: 'foo',
firstName: 'Foobar',
lastName: 'Bazman'
};
const info = await db.bulkDocs({docs: [ddoc, doc]});
ddoc._rev = info[0].rev;
let res = await db.query('myview');
res.rows.should.deep.equal([
{id: 'foo', key: 'Foobar', value: null}
]);
// Cache created for myview along with an entry in the meta doc
const metaDoc0 = await db.get('_local/mrviews');
Object.keys(metaDoc0.views['myview/myview']).should.have.length(1);
Object.keys(db._cachedViews).should.have.length(1);
// Update myview to have a different map function
ddoc.views.myview.map = function (doc) {
emit(doc.lastName);
}.toString();
await db.put(ddoc);
res = await db.query('myview');
res.rows.should.deep.equal([
{id: 'foo', key: 'Bazman', value: null}
]);
// New cache created for myview along with a new entry in the meta doc
const metaDoc1 = await db.get('_local/mrviews');
Object.keys(metaDoc1.views['myview/myview']).should.have.length(2);
Object.keys(db._cachedViews).should.have.length(2);
await db.viewCleanup();
// View cleanup does not remove the cache for the old version of myview
const metaDoc2 = await db.get('_local/mrviews');
metaDoc2.should.deep.equal(metaDoc1);
Object.keys(db._cachedViews).should.have.length(2);
// Add new ddoc with view that matches original version of myview
const newDdoc = {
_id: '_design/mynewview',
views: {
mynewview: {
map: function (doc) {
emit(doc.firstName);
}.toString()
}
}
};
await db.put(newDdoc);
res = await db.query('mynewview');
res.rows.should.deep.equal([
{id: 'foo', key: 'Foobar', value: null}
]);
// Meta doc not updated since mynewview is a cache collision with original myview cache
const metaDoc3 = await db.get('_local/mrviews');
metaDoc3.should.deep.equal(metaDoc2);
Object.keys(db._cachedViews).should.have.length(2);
// Remove myview from ddoc
const ddocClearViews = await db.get(ddoc._id);
ddocClearViews.views = {};
await db.put(ddocClearViews);
await db.viewCleanup();
// No updates to the meta doc
const metaDoc4 = await db.get('_local/mrviews');
metaDoc4.should.deep.equal(metaDoc3);
// Both cached views are removed (even though one was still "used" by mynewview).
Object.keys(db._cachedViews).should.have.length(0);
});
To break this down a little more, when a local persistent view is first queried, a new db is created to maintain the view data and a reference to this db/view is added to the parent db's cachedViews array. Also, an entry for the view db is added to the _local/mrviews doc for the parent db and a destroyed hook is added to the view db to remove the cache entry when the view db is deleted.
When a new revision is created for an existing view index (e.g. updated map function) and the view is queried, there will be a cache miss because the cache is keyed by the actual map/reduce code for the view. This means that a new view db will be created and cached following the same logic as above. The revisions also accumulate in the _local/mrviews doc. E.g.:
{
"views": {
"medic-client/contacts_by_freetext": {
"medic-user-chw-mrview-5767fa5694897717c301bcae650cf19a": true,
"medic-user-chw-mrview-08ec98c1527e69c9db02a38d33ef89b3": true,
"medic-user-chw-mrview-4644404bae3d2481974ac0b17e00cec1": true
}
}
}
Then, when viewCleanup is triggered, the contents of this metadoc drives what happens. All the design docs listed in the metadoc are retrieved from the db. Then, for each cached view listed in the metadoc, it checks to see if there is still a design doc/view in the database with a matching name. If the design doc and/or the view have been removed, the view db from the metadoc is destroyed. This will trigger the destroyed hook to also remove the cache entry.
But, all this viewCleanup seems solely focused on the design/view name. Having multiple view db entries in the metadoc for a single view does not seem to be considered sufficient cause for deleting the extra view dbs. So, these revisions continue to accumulate until the view and/or design is removed.
Tangentially related (and also illustrated by the above test case) is that since _cachedViews is keyed on the "view signature" (from the map/reduce functions) then other views/designs that have the same view signature would hit on the same cache entry and a duplicate view db will not be build. However, the viewCleanup logic operates based on the _local/mrviews contents which are keyed by the design/view name this can result in unexpected behavior as noted in the test where a call to viewCleanup could end up forcing an existing view to be rebuilt.
- Ngôn ngữ chính
- JavaScript
- Star
- 17.6k
- Fork
- 1.5k
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của apache/pouchdb
-
elemMatch throws TypeError: value is null if array has null elementCó thể đã có người làm @henyanagar đã nhận 142 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
unref event is not documentedĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
Validate database namesĐang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 55/100
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 65/100
Tất cả issue của apache/pouchdb
Issue tương tự
-
ai-driven-fuzzing bug Cosmetic
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
linagora/cozy-client#1831 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
ai-driven-fuzzing bug Major
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 80/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[ordering-review] workshop order graph: 1 confirmed ordering problem, 3 cycles are false positivesĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
githubnext/gh-aw-workshop#5331 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Remove: 21 SexturyĐang mởchannels:remove check:passed
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 65/100
Maintainer thường phản hồi trong vòng 4 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày