MergeNotebox takes a "changes" Notefile from box.GetChanges, and integrates those changes into the current notebox. NOTE that the "fromBoxFile" should be assumed to be modified and then freed by this method, so the caller must not retain any references.
(ctx context.Context, fromBoxfile *Notefile)
| 1497 | // changes into the current notebox. NOTE that the "fromBoxFile" should be assumed |
| 1498 | // to be modified and then freed by this method, so the caller must not retain any references. |
| 1499 | func (box *Notebox) MergeNotebox(ctx context.Context, fromBoxfile *Notefile) (err error) { |
| 1500 | |
| 1501 | // Get pointers to our notefile |
| 1502 | boxfile := box.Notefile() |
| 1503 | |
| 1504 | // First, purge anything pertaining to the local instances, so that we don't inadvertently |
| 1505 | // delete notes that are our only pointers to local storage. We simply don't allow remote |
| 1506 | // endpoints to modify our local instance notes. |
| 1507 | for noteID := range fromBoxfile.Notes { |
| 1508 | if noteID == "" { |
| 1509 | // This shouldn't happen, but it occasionally does. I believe it is a race condition |
| 1510 | // on the notecard with someone adding or removing a notefile concurrently with the |
| 1511 | // sync task doing an pushBoxToNotehub(). |
| 1512 | logDebug(ctx, "MergeNotebox: empty notefileID (changed notebox) coming from Notecard") |
| 1513 | continue |
| 1514 | } |
| 1515 | isInstance, endpointID, _ := parseCompositeNoteID(noteID) |
| 1516 | if isInstance && endpointID == box.instance.endpointID { |
| 1517 | delete(fromBoxfile.Notes, noteID) |
| 1518 | } |
| 1519 | } |
| 1520 | |
| 1521 | // Next, merge what's remaining. This will add and remove remote all remote notefile changes, |
| 1522 | // as well as updating the global notefile notes that contain NotefileInfo |
| 1523 | err = boxfile.MergeNotefile(ctx, nil, fromBoxfile) |
| 1524 | if err != nil { |
| 1525 | return err |
| 1526 | } |
| 1527 | |
| 1528 | // Purge tombstones from this file, since much of what was replicated inward likely |
| 1529 | // had been tombstones, some of which may no longer be needed. |
| 1530 | boxfile.PurgeTombstones(note.DefaultHubEndpointID) |
| 1531 | |
| 1532 | // If any of the notefile info docs are updated, flush the cached versions of same |
| 1533 | // if it's possible to do so. We do this by forcibly removing refcnt==0 from memory. |
| 1534 | _, _ = box.checkpoint(ctx, true, false, true, false) |
| 1535 | |
| 1536 | // Now that this is done, lock the world, both the boxfile and the box's notefile, |
| 1537 | // because in essence this is a custom merge procedure. |
| 1538 | box.Openfile().lock.Lock() |
| 1539 | boxfile.lock.Lock() |
| 1540 | |
| 1541 | // Now, iterate over all notes in the notefile looking for work to do. The rules we need to |
| 1542 | // pay attention to are (in this order), |
| 1543 | // 1. If ANY global notefile is deleted, then ALL instances must eventually go away |
| 1544 | // 2. If a global notefile note exists ANYWHERE, it must eventually appear on ALL instances |
| 1545 | // First, classify the entire contents of the notebox |
| 1546 | didSomething := false |
| 1547 | existsLocally := []string{} |
| 1548 | deletedLocally := []string{} |
| 1549 | existsGloballyNoteID := []string{} |
| 1550 | deletedGloballyNoteID := []string{} |
| 1551 | for noteID, note := range boxfile.Notes { |
| 1552 | if noteID == "" { |
| 1553 | // This shouldn't happen, but it occasionally does. I believe it is a race condition |
| 1554 | // on the notecard with someone adding or removing a notefile concurrently with the |
| 1555 | // sync task doing an pushBoxToNotehub(). Here we just clean it up. |
| 1556 | logDebug(ctx, "MergeNotebox: empty notefileID in merged contents of Notecard and Notehub") |
no test coverage detected