fix most gc bugs - test suite running again
This commit is contained in:
parent
d915c8dd13
commit
ef6eb08335
@ -1,6 +1,7 @@
|
||||
import { getStruct } from '../Util/structReferences.js'
|
||||
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
||||
import { logID } from './messageToString.js'
|
||||
import GC from '../Struct/GC.js';
|
||||
|
||||
class MissingEntry {
|
||||
constructor (decoder, missing, struct) {
|
||||
@ -24,7 +25,14 @@ function _integrateRemoteStructHelper (y, struct) {
|
||||
if (y.ss.getState(id.user) > id.clock) {
|
||||
return
|
||||
}
|
||||
struct._integrate(y)
|
||||
if (struct.constructor === GC || (struct._parent.constructor !== GC && struct._parent._deleted === false)) {
|
||||
// Is either a GC or Item with an undeleted parent
|
||||
// save to integrate
|
||||
struct._integrate(y)
|
||||
} else {
|
||||
// Is an Item. parent was deleted.
|
||||
struct._gc(y)
|
||||
}
|
||||
let msu = y._missingStructs.get(id.user)
|
||||
if (msu != null) {
|
||||
let clock = id.clock
|
||||
|
@ -52,7 +52,7 @@ export default class DeleteStore extends Tree {
|
||||
if (rightD !== null && rightD._id.user === id.user) {
|
||||
if (rightD._id.clock < id.clock + length && id.clock <= rightD._id.clock && id.clock + length < rightD._id.clock + rightD.len) { // we only consider the case where we resize the node
|
||||
const d = id.clock + length - rightD._id.clock
|
||||
rightD._id.clock += d
|
||||
rightD._id = new ID(rightD._id.user, rightD._id.clock + d)
|
||||
rightD.len -= d
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import Tree from '../Util/Tree.js'
|
||||
import RootID from '../Util/ID/RootID.js'
|
||||
import { getStruct } from '../Util/structReferences.js'
|
||||
import { logID } from '../MessageHandler/messageToString.js'
|
||||
import GC from '../Struct/GC.js'
|
||||
|
||||
export default class OperationStore extends Tree {
|
||||
constructor (y) {
|
||||
@ -11,17 +12,25 @@ export default class OperationStore extends Tree {
|
||||
logTable () {
|
||||
const items = []
|
||||
this.iterate(null, null, function (item) {
|
||||
items.push({
|
||||
id: logID(item),
|
||||
origin: logID(item._origin === null ? null : item._origin._lastId),
|
||||
left: logID(item._left === null ? null : item._left._lastId),
|
||||
right: logID(item._right),
|
||||
right_origin: logID(item._right_origin),
|
||||
parent: logID(item._parent),
|
||||
parentSub: item._parentSub,
|
||||
deleted: item._deleted,
|
||||
content: JSON.stringify(item._content)
|
||||
})
|
||||
if (item.constructor === GC) {
|
||||
items.push({
|
||||
id: logID(item),
|
||||
content: item._length,
|
||||
deleted: 'GC'
|
||||
})
|
||||
} else {
|
||||
items.push({
|
||||
id: logID(item),
|
||||
origin: logID(item._origin === null ? null : item._origin._lastId),
|
||||
left: logID(item._left === null ? null : item._left._lastId),
|
||||
right: logID(item._right),
|
||||
right_origin: logID(item._right_origin),
|
||||
parent: logID(item._parent),
|
||||
parentSub: item._parentSub,
|
||||
deleted: item._deleted,
|
||||
content: JSON.stringify(item._content)
|
||||
})
|
||||
}
|
||||
})
|
||||
console.table(items)
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { getStructReference } from '../Util/structReferences.js'
|
||||
import { RootFakeUserID } from '../Util/ID/RootID.js'
|
||||
import ID from '../Util/ID/ID.js'
|
||||
|
||||
// TODO should have the same base class as Item
|
||||
export default class GC {
|
||||
constructor () {
|
||||
this._id = null
|
||||
@ -9,7 +13,38 @@ export default class GC {
|
||||
return true
|
||||
}
|
||||
|
||||
integrate () {
|
||||
_integrate (y) {
|
||||
const id = this._id
|
||||
const userState = y.ss.getState(id.user)
|
||||
if (id.clock === userState) {
|
||||
y.ss.setState(id.user, id.clock + this._length)
|
||||
}
|
||||
y.ds.mark(this._id, this._length, true)
|
||||
let n = y.os.put(this)
|
||||
const prev = n.prev().val
|
||||
if (prev !== null && prev.constructor === GC && prev._id.user === n.val._id.user && prev._id.clock + prev._length === n.val._id.clock) {
|
||||
// TODO: do merging for all items!
|
||||
prev._length += n.val._length
|
||||
y.os.delete(n.val._id)
|
||||
n = prev
|
||||
}
|
||||
if (n.val) {
|
||||
n = n.val
|
||||
}
|
||||
const next = y.os.findNext(n._id)
|
||||
if (next !== null && next.constructor === GC && next._id.user === n._id.user && next._id.clock === n._id.clock + n._length) {
|
||||
n._length += next._length
|
||||
y.os.delete(next._id)
|
||||
}
|
||||
|
||||
if (id.user !== RootFakeUserID) {
|
||||
if (y.connector !== null && (y.connector._forwardAppliedStructs || id.user === y.userID)) {
|
||||
y.connector.broadcastStruct(this)
|
||||
}
|
||||
if (y.persistence !== null) {
|
||||
y.persistence.saveStruct(y, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,8 +72,17 @@ export default class GC {
|
||||
* @private
|
||||
*/
|
||||
_fromBinary (y, decoder) {
|
||||
this._id = decoder.readID()
|
||||
const id = decoder.readID()
|
||||
this._id = id
|
||||
this._length = decoder.readVarUint()
|
||||
return []
|
||||
}
|
||||
const missing = []
|
||||
if (y.ss.getState(id.user) < id.clock) {
|
||||
missing.push(new ID(id.user, id.clock - 1))
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
_splitAt () {
|
||||
return this
|
||||
}
|
||||
}
|
@ -240,27 +240,11 @@ export default class Item {
|
||||
_gcChildren (y) {}
|
||||
|
||||
_gc (y) {
|
||||
y.ds.mark(this._id, this._length, true)
|
||||
const gc = new GC()
|
||||
gc._id = this._id
|
||||
gc._length = this._length
|
||||
y.os.delete(this._id)
|
||||
let n = y.os.put(gc)
|
||||
const prev = n.prev().val
|
||||
if (prev !== null && prev.constructor === GC && prev._id.user === n.val._id.user && prev._id.clock + prev._length === n.val._id.clock) {
|
||||
// TODO: do merging for all items!
|
||||
prev._length += n.val._length
|
||||
y.os.delete(n.val._id)
|
||||
n = prev
|
||||
}
|
||||
if (n.val) {
|
||||
n = n.val
|
||||
}
|
||||
const next = y.os.findNext(n._id)
|
||||
if (next !== null && next.constructor === GC && next._id.user === n._id.user && next._id.clock === n._id.clock + n._length) {
|
||||
n._length += next._length
|
||||
y.os.delete(n._id)
|
||||
}
|
||||
gc._integrate(y)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -511,9 +495,19 @@ export default class Item {
|
||||
}
|
||||
} else if (this._parent === null) {
|
||||
if (this._origin !== null) {
|
||||
this._parent = this._origin._parent
|
||||
if (this._origin.constructor === GC) {
|
||||
// if origin is a gc, set parent also gc'd
|
||||
this._parent = this._origin
|
||||
} else {
|
||||
this._parent = this._origin._parent
|
||||
}
|
||||
} else if (this._right_origin !== null) {
|
||||
this._parent = this._right_origin._parent
|
||||
// if origin is a gc, set parent also gc'd
|
||||
if (this._right_origin.constructor === GC) {
|
||||
this._parent = this._right_origin
|
||||
} else {
|
||||
this._parent = this._right_origin._parent
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info & 0b1000) {
|
||||
|
@ -8,6 +8,7 @@ import ItemJSON from '../src/Struct/ItemJSON.js'
|
||||
import ItemString from '../src/Struct/ItemString.js'
|
||||
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
||||
import Quill from 'quill'
|
||||
import GC from '../src/Struct/GC.js';
|
||||
|
||||
export const Y = _Y
|
||||
|
||||
@ -110,13 +111,22 @@ export async function compareUsers (t, users) {
|
||||
var data = {}
|
||||
let ops = []
|
||||
u.os.iterate(null, null, function (op) {
|
||||
const json = {
|
||||
id: op._id,
|
||||
left: op._left === null ? null : op._left._lastId,
|
||||
right: op._right === null ? null : op._right._id,
|
||||
length: op._length,
|
||||
deleted: op._deleted,
|
||||
parent: op._parent._id
|
||||
let json
|
||||
if (op.constructor === GC) {
|
||||
json = {
|
||||
type: 'GC',
|
||||
id: op._id,
|
||||
length: op._length
|
||||
}
|
||||
} else {
|
||||
json = {
|
||||
id: op._id,
|
||||
left: op._left === null ? null : op._left._lastId,
|
||||
right: op._right === null ? null : op._right._id,
|
||||
length: op._length,
|
||||
deleted: op._deleted,
|
||||
parent: op._parent._id
|
||||
}
|
||||
}
|
||||
if (op instanceof ItemJSON || op instanceof ItemString) {
|
||||
json.content = op._content
|
||||
@ -186,7 +196,7 @@ export async function initArrays (t, opts) {
|
||||
y.dom = dom
|
||||
y.on('afterTransaction', function () {
|
||||
for (let missing of y._missingStructs.values()) {
|
||||
if (Array.from(missing.values()).length > 0) {
|
||||
if (missing.size > 0) {
|
||||
console.error(new Error('Test check in "afterTransaction": missing should be empty!'))
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user