github.com/simpleiot/simpleiot@v0.18.3/client/sync.go (about) 1 package client 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/nats-io/nats.go" 10 "github.com/simpleiot/simpleiot/data" 11 ) 12 13 // Sync represents an sync node config 14 type Sync struct { 15 ID string `node:"id"` 16 Parent string `node:"parent"` 17 Description string `point:"description"` 18 URI string `point:"uri"` 19 AuthToken string `point:"authToken"` 20 Period int `point:"period"` 21 Disabled bool `point:"disabled"` 22 SyncCount int `point:"syncCount"` 23 SyncCountReset bool `point:"syncCountReset"` 24 } 25 26 type newEdge struct { 27 parent string 28 id string 29 local bool 30 } 31 32 // SyncClient is a SIOT client used to handle upstream connections 33 type SyncClient struct { 34 nc *nats.Conn 35 ncLocal *nats.Conn 36 ncRemote *nats.Conn 37 rootLocal data.NodeEdge 38 rootRemote data.NodeEdge 39 config Sync 40 stop chan struct{} 41 newPoints chan NewPoints 42 newEdgePoints chan NewPoints 43 subRemoteNodePoints map[string]*nats.Subscription 44 subRemoteEdgePoints map[string]*nats.Subscription 45 subRemoteUp *nats.Subscription 46 chConnected chan bool 47 initialSub bool 48 chNewEdge chan newEdge 49 } 50 51 // NewSyncClient constructor 52 func NewSyncClient(nc *nats.Conn, config Sync) Client { 53 return &SyncClient{ 54 nc: nc, 55 config: config, 56 stop: make(chan struct{}), 57 newPoints: make(chan NewPoints), 58 newEdgePoints: make(chan NewPoints), 59 chConnected: make(chan bool), 60 subRemoteNodePoints: make(map[string]*nats.Subscription), 61 subRemoteEdgePoints: make(map[string]*nats.Subscription), 62 chNewEdge: make(chan newEdge), 63 } 64 } 65 66 // Run the main logic for this client and blocks until stopped 67 func (up *SyncClient) Run() error { 68 // create a new NATs connection to the local server as we need to 69 // turn echo off 70 uri, token, err := GetNatsURI(up.nc) 71 if err != nil { 72 return fmt.Errorf("Error getting NATS URI: %v", err) 73 } 74 75 opts := EdgeOptions{ 76 URI: uri, 77 AuthToken: token, 78 NoEcho: true, 79 Connected: func() { 80 log.Printf("Sync: %v: Local Connected: %v\n", up.config.Description, uri) 81 }, 82 Disconnected: func() { 83 log.Printf("Sync: %v: Local Disconnected\n", up.config.Description) 84 }, 85 Reconnected: func() { 86 log.Printf("Sync: %v: Local Reconnected\n", up.config.Description) 87 }, 88 Closed: func() { 89 log.Printf("Sync: %v: Local Closed\n", up.config.Description) 90 }, 91 } 92 93 up.ncLocal, err = EdgeConnect(opts) 94 if err != nil { 95 return fmt.Errorf("Error connection to local NATS: %v", err) 96 } 97 98 chLocalNodePoints := make(chan NewPoints) 99 chLocalEdgePoints := make(chan NewPoints) 100 101 subLocalNodePoints, err := up.ncLocal.Subscribe(SubjectNodeAllPoints(), func(msg *nats.Msg) { 102 nodeID, points, err := DecodeNodePointsMsg(msg) 103 104 if err != nil { 105 log.Println("Error decoding point:", err) 106 return 107 } 108 109 chLocalNodePoints <- NewPoints{ID: nodeID, Points: points} 110 111 }) 112 if err != nil { 113 log.Println("SyncClient: error subscribing:", err) 114 } 115 116 subLocalEdgePoints, err := up.ncLocal.Subscribe(SubjectEdgeAllPoints(), func(msg *nats.Msg) { 117 nodeID, parentID, points, err := DecodeEdgePointsMsg(msg) 118 119 if err != nil { 120 log.Println("Error decoding point:", err) 121 return 122 } 123 124 chLocalEdgePoints <- NewPoints{ID: nodeID, Parent: parentID, Points: points} 125 126 for _, p := range points { 127 if p.Type == data.PointTypeTombstone && p.Value == 0 { 128 // a new node was likely created, make sure we watch it 129 up.chNewEdge <- newEdge{parent: parentID, id: nodeID, local: true} 130 } 131 } 132 }) 133 if err != nil { 134 log.Println("SyncClient: error subscribing:", err) 135 } 136 137 checkPeriod := func() { 138 if up.config.Period < 1 { 139 up.config.Period = 20 140 points := data.Points{ 141 {Type: data.PointTypePeriod, Value: float64(up.config.Period)}, 142 } 143 144 err = SendPoints(up.nc, SubjectNodePoints(up.config.ID), points, false) 145 if err != nil { 146 log.Println("Error resetting sync sync count:", err) 147 } 148 } 149 } 150 151 checkPeriod() 152 153 syncTicker := time.NewTicker(time.Second * 10) 154 syncTicker.Stop() 155 156 connectTimer := time.NewTimer(time.Millisecond * 10) 157 158 up.rootLocal, err = GetRootNode(up.nc) 159 if err != nil { 160 return fmt.Errorf("Error getting root node: %v", err) 161 } 162 163 connected := false 164 up.initialSub = false 165 166 done: 167 for { 168 select { 169 case <-up.stop: 170 log.Println("Stopping upstream client:", up.config.Description) 171 break done 172 case <-connectTimer.C: 173 err := up.connect() 174 if err != nil { 175 log.Printf("Sync connect failure: %v: %v\n", 176 up.config.Description, err) 177 connectTimer.Reset(30 * time.Second) 178 } 179 case <-syncTicker.C: 180 err := up.syncNode("root", up.rootLocal.ID) 181 if err != nil { 182 log.Println("Error syncing:", err) 183 } 184 185 case conn := <-up.chConnected: 186 connected = conn 187 if conn { 188 syncTicker.Reset(time.Duration(up.config.Period) * time.Second) 189 err := up.syncNode("root", up.rootLocal.ID) 190 if err != nil { 191 log.Println("Error syncing:", err) 192 } 193 194 if !up.initialSub { 195 // set up initial subscriptions to remote nodes 196 err = up.subscribeRemoteNode(up.rootLocal.Parent, up.rootLocal.ID) 197 if err != nil { 198 log.Println("Sync: initial sub failed:", err) 199 } else { 200 up.initialSub = true 201 } 202 } 203 } else { 204 syncTicker.Stop() 205 // the following is required in case a new server 206 // is set up which may have a new root 207 up.rootRemote = data.NodeEdge{} 208 } 209 case pts := <-chLocalNodePoints: 210 if connected { 211 err = SendNodePoints(up.ncRemote, pts.ID, pts.Points, false) 212 if err != nil { 213 log.Println("Error sending node points to remote system:", err) 214 } 215 } 216 case pts := <-chLocalEdgePoints: 217 if connected { 218 err = SendEdgePoints(up.ncRemote, pts.ID, pts.Parent, pts.Points, false) 219 if err != nil { 220 log.Println("Error sending edge points to remote system:", err) 221 } 222 } 223 case pts := <-up.newPoints: 224 err := data.MergePoints(pts.ID, pts.Points, &up.config) 225 if err != nil { 226 log.Println("error merging new points:", err) 227 } 228 229 for _, p := range pts.Points { 230 switch p.Type { 231 case data.PointTypeURI, 232 data.PointTypeAuthToken, 233 data.PointTypeDisabled: 234 // we need to restart the sync connection 235 up.disconnect() 236 connectTimer.Reset(10 * time.Millisecond) 237 case data.PointTypePeriod: 238 checkPeriod() 239 if connected { 240 syncTicker.Reset(time.Duration(up.config.Period) * 241 time.Second) 242 } 243 } 244 } 245 246 if up.config.SyncCountReset { 247 up.config.SyncCount = 0 248 up.config.SyncCountReset = false 249 250 points := data.Points{ 251 {Type: data.PointTypeSyncCount, Value: 0}, 252 {Type: data.PointTypeSyncCountReset, Value: 0}, 253 } 254 255 err = SendPoints(up.nc, SubjectNodePoints(up.config.ID), points, false) 256 if err != nil { 257 log.Println("Error resetting sync sync count:", err) 258 } 259 } 260 261 case pts := <-up.newEdgePoints: 262 err := data.MergeEdgePoints(pts.ID, pts.Parent, pts.Points, &up.config) 263 if err != nil { 264 log.Println("error merging new points:", err) 265 } 266 case edge := <-up.chNewEdge: 267 if !edge.local { 268 // a new remote node was created, if it does not exist here, 269 // create it 270 271 // if parent is upstream root, then we don't worry about it 272 if edge.parent == up.rootRemote.ID { 273 break 274 } 275 276 nodes, err := GetNodes(up.ncLocal, edge.parent, edge.id, "", true) 277 if err != nil { 278 log.Println("Error getting local node:", err) 279 break 280 } 281 282 if len(nodes) > 0 { 283 // local node already exists, so don't do anything 284 break 285 } 286 // local node does not exist, so get the remote and send it 287 fetchAgain: 288 // edge points are sent first, so it may take a bit before we see 289 // the node points 290 time.Sleep(10 * time.Millisecond) 291 nodes, err = GetNodes(up.ncRemote, edge.parent, edge.id, "", true) 292 if err != nil { 293 log.Println("Error getting node:", err) 294 break 295 } 296 for _, n := range nodes { 297 // if type is not populated yet, try again 298 if n.Type == "" { 299 goto fetchAgain 300 } 301 err := up.sendNodesLocal(n) 302 if err != nil { 303 log.Println("Error chNewEdge sendNodesLocal:", err) 304 } 305 } 306 } 307 308 err = up.subscribeRemoteNode(edge.parent, edge.id) 309 if err != nil { 310 log.Println("Error subscribing to new edge:", err) 311 } 312 } 313 } 314 315 // clean up 316 err = subLocalNodePoints.Unsubscribe() 317 if err != nil { 318 log.Println("Error unsubscribing node points from local bus:", err) 319 } 320 321 err = subLocalEdgePoints.Unsubscribe() 322 if err != nil { 323 log.Println("Error unsubscribing edge points from local bus:", err) 324 } 325 326 up.disconnect() 327 up.ncLocal.Close() 328 329 return nil 330 } 331 332 // Stop sends a signal to the Run function to exit 333 func (up *SyncClient) Stop(_ error) { 334 close(up.stop) 335 } 336 337 // Points is called by the Manager when new points for this 338 // node are received. 339 func (up *SyncClient) Points(nodeID string, points []data.Point) { 340 up.newPoints <- NewPoints{nodeID, "", points} 341 } 342 343 // EdgePoints is called by the Manager when new edge points for this 344 // node are received. 345 func (up *SyncClient) EdgePoints(nodeID, parentID string, points []data.Point) { 346 up.newEdgePoints <- NewPoints{nodeID, parentID, points} 347 } 348 349 func (up *SyncClient) connect() error { 350 if up.config.Disabled { 351 log.Printf("Sync %v disabled", up.config.Description) 352 return nil 353 } 354 355 opts := EdgeOptions{ 356 URI: up.config.URI, 357 AuthToken: up.config.AuthToken, 358 NoEcho: true, 359 Connected: func() { 360 up.chConnected <- true 361 log.Printf("Sync: %v: Remote Connected: %v\n", 362 up.config.Description, up.config.URI) 363 }, 364 Disconnected: func() { 365 up.chConnected <- false 366 log.Printf("Sync: %v: Remote Disconnected\n", up.config.Description) 367 }, 368 Reconnected: func() { 369 up.chConnected <- true 370 log.Printf("Sync: %v: Remote Reconnected\n", up.config.Description) 371 }, 372 Closed: func() { 373 log.Printf("Sync: %v: Remote Closed\n", up.config.Description) 374 }, 375 } 376 377 var err error 378 up.ncRemote, err = EdgeConnect(opts) 379 380 if err != nil { 381 return fmt.Errorf("Error connection to upstream NATS: %v", err) 382 } 383 384 return nil 385 } 386 387 func (up *SyncClient) subscribeRemoteNodePoints(id string) error { 388 if _, ok := up.subRemoteNodePoints[id]; !ok { 389 var err error 390 up.subRemoteNodePoints[id], err = up.ncRemote.Subscribe(SubjectNodePoints(id), func(msg *nats.Msg) { 391 nodeID, points, err := DecodeNodePointsMsg(msg) 392 if err != nil { 393 log.Println("Error decoding point:", err) 394 return 395 } 396 397 err = SendNodePoints(up.ncLocal, nodeID, points, false) 398 if err != nil { 399 log.Println("Error sending node points to remote system:", err) 400 } 401 }) 402 403 if err != nil { 404 return err 405 } 406 } 407 408 return nil 409 } 410 411 func (up *SyncClient) subscribeRemoteEdgePoints(parent, id string) error { 412 if _, ok := up.subRemoteEdgePoints[id]; !ok { 413 var err error 414 key := id + ":" + parent 415 up.subRemoteEdgePoints[key], err = up.ncRemote.Subscribe(SubjectEdgePoints(id, parent), 416 func(msg *nats.Msg) { 417 nodeID, parentID, points, err := DecodeEdgePointsMsg(msg) 418 if err != nil { 419 log.Println("Error decoding point:", err) 420 return 421 } 422 423 err = SendEdgePoints(up.ncLocal, nodeID, parentID, points, false) 424 if err != nil { 425 log.Println("Error sending edge points to remote system:", err) 426 } 427 }) 428 429 if err != nil { 430 return err 431 } 432 } 433 return nil 434 } 435 436 func (up *SyncClient) subscribeRemoteNode(parent, id string) error { 437 err := up.subscribeRemoteNodePoints(id) 438 if err != nil { 439 return err 440 } 441 442 err = up.subscribeRemoteEdgePoints(parent, id) 443 if err != nil { 444 return err 445 } 446 447 // we walk through all local nodes and and subscribe to remote changes 448 children, err := GetNodes(up.ncLocal, id, "all", "", true) 449 if err != nil { 450 return err 451 } 452 453 for _, c := range children { 454 err := up.subscribeRemoteNode(c.Parent, c.ID) 455 if err != nil { 456 return err 457 } 458 } 459 460 return nil 461 } 462 463 func (up *SyncClient) disconnect() { 464 for key, sub := range up.subRemoteNodePoints { 465 err := sub.Unsubscribe() 466 if err != nil { 467 log.Println("Error unsubscribing from remote:", err) 468 } 469 delete(up.subRemoteNodePoints, key) 470 } 471 472 for key, sub := range up.subRemoteEdgePoints { 473 err := sub.Unsubscribe() 474 if err != nil { 475 log.Println("Error unsubscribing from remote:", err) 476 } 477 delete(up.subRemoteEdgePoints, key) 478 } 479 480 up.initialSub = false 481 if up.subRemoteUp != nil { 482 err := up.subRemoteUp.Unsubscribe() 483 if err != nil { 484 log.Println("subRemoteUp.Unsubscribe() error:", err) 485 } 486 up.subRemoteUp = nil 487 } 488 489 if up.ncRemote != nil { 490 up.ncRemote.Close() 491 up.ncRemote = nil 492 up.rootRemote = data.NodeEdge{} 493 } 494 } 495 496 // sendNodesRemote is used to send node and children over nats 497 // from one NATS server to another. Typically from the current instance 498 // to an upstream. 499 func (up *SyncClient) sendNodesRemote(node data.NodeEdge) error { 500 if node.Parent == "root" { 501 node.Parent = up.rootRemote.ID 502 } 503 504 err := SendNode(up.ncRemote, node, up.config.ID) 505 if err != nil { 506 return err 507 } 508 509 // process child nodes 510 childNodes, err := GetNodes(up.nc, node.ID, "all", "", false) 511 if err != nil { 512 return fmt.Errorf("Error getting node children: %v", err) 513 } 514 515 for _, childNode := range childNodes { 516 err := up.sendNodesRemote(childNode) 517 518 if err != nil { 519 return fmt.Errorf("Error sending child node: %v", err) 520 } 521 } 522 523 return nil 524 } 525 526 // sendNodesLocal is used to send node and children over nats 527 // from one NATS server to another. Typically from the current instance 528 // to an upstream. 529 func (up *SyncClient) sendNodesLocal(node data.NodeEdge) error { 530 err := SendNode(up.ncLocal, node, up.config.ID) 531 if err != nil { 532 return err 533 } 534 535 // process child nodes 536 childNodes, err := GetNodes(up.nc, node.ID, "all", "", false) 537 if err != nil { 538 return fmt.Errorf("Error getting node children: %v", err) 539 } 540 541 for _, childNode := range childNodes { 542 err := up.sendNodesLocal(childNode) 543 544 if err != nil { 545 return fmt.Errorf("Error sending child node: %v", err) 546 } 547 } 548 549 return nil 550 } 551 552 func (up *SyncClient) syncNode(parent, id string) error { 553 var err error 554 if up.rootRemote.ID == "" { 555 up.rootRemote, err = GetRootNode(up.ncRemote) 556 if err != nil { 557 log.Printf("Sync: %v, error getting upstream root: %v\n", up.config.Description, err) 558 return fmt.Errorf("Error getting upstream root: %v", err) 559 } 560 } 561 562 if up.subRemoteUp == nil { 563 subject := fmt.Sprintf("up.%v.*.*", up.rootLocal.ID) 564 up.subRemoteUp, err = up.ncRemote.Subscribe(subject, func(msg *nats.Msg) { 565 _, id, parent, points, err := DecodeUpEdgePointsMsg(msg) 566 if err != nil { 567 log.Println("Error decoding remote up points:", err) 568 } else { 569 for _, p := range points { 570 if p.Type == data.PointTypeTombstone && 571 p.Value == 0 { 572 // we have a new node 573 up.chNewEdge <- newEdge{ 574 parent: parent, id: id} 575 } 576 } 577 } 578 }) 579 580 if err != nil { 581 log.Println("Error subscribing to remote up...:", err) 582 } 583 } 584 585 // Why do we do this? 586 if parent == "root" { 587 parent = "all" 588 } 589 590 nodeLocals, err := GetNodes(up.nc, parent, id, "", true) 591 if err != nil { 592 return fmt.Errorf("Error getting local node: %v", err) 593 } 594 595 if len(nodeLocals) == 0 { 596 return errors.New("local nodes not found") 597 } 598 599 nodeLocal := nodeLocals[0] 600 601 nodeUps, upErr := GetNodes(up.ncRemote, parent, id, "", true) 602 if upErr != nil { 603 if upErr != data.ErrDocumentNotFound { 604 return fmt.Errorf("Error getting upstream root node: %v", upErr) 605 } 606 } 607 608 var nodeUp data.NodeEdge 609 610 nodeDeleted := false 611 nodeFound := len(nodeUps) > 0 612 613 if nodeFound { 614 nodeDeleted = true 615 for _, n := range nodeUps { 616 ts, _ := n.IsTombstone() 617 if !ts { 618 nodeDeleted = false 619 break 620 } 621 } 622 } 623 624 if nodeDeleted { 625 nodeUp = nodeUps[0] 626 // restore a node on the upstream 627 // update the local tombstone timestamp so it is newer than the remote tombstone timestamp 628 log.Printf("Sync: undeleting remote node: %v:%v\n", nodeUp.Parent, nodeUp.ID) 629 pTS := data.Point{Time: time.Now(), Type: data.PointTypeTombstone, Value: 0} 630 err := SendEdgePoint(up.ncRemote, nodeUp.ID, nodeUp.Parent, pTS, true) 631 if err != nil { 632 return fmt.Errorf("Error undeleting upstream node: %v", err) 633 } 634 635 // FIXME, remove this return 636 return nil 637 } 638 639 if !nodeFound { 640 log.Printf("Sync node %v does not exist, sending\n", nodeLocal.Desc()) 641 err := up.sendNodesRemote(nodeLocal) 642 if err != nil { 643 return fmt.Errorf("Error sending node upstream: %w", err) 644 } 645 646 err = up.subscribeRemoteNode(nodeLocal.ID, nodeLocal.Parent) 647 if err != nil { 648 return fmt.Errorf("Error subscribing to node changes: %w", err) 649 } 650 651 return nil 652 } 653 654 nodeUp = nodeUps[0] 655 656 if nodeLocal.ID == up.rootLocal.ID { 657 // we need to back out the edge points from the hash as don't want to sync those 658 for _, p := range nodeUp.EdgePoints { 659 nodeUp.Hash ^= p.CRC() 660 } 661 662 for _, p := range nodeLocal.EdgePoints { 663 nodeLocal.Hash ^= p.CRC() 664 } 665 } 666 667 if nodeUp.Hash == nodeLocal.Hash { 668 // we're good! 669 return nil 670 } 671 672 // only increment count once during sync 673 if nodeLocal.ID == up.rootLocal.ID { 674 up.config.SyncCount++ 675 points := data.Points{ 676 {Type: data.PointTypeSyncCount, Value: float64(up.config.SyncCount)}, 677 } 678 679 err = SendPoints(up.nc, SubjectNodePoints(up.config.ID), points, false) 680 if err != nil { 681 log.Println("Error resetting sync sync count:", err) 682 } 683 } 684 685 log.Printf("sync %v: syncing node: %v, hash up: 0x%x, down: 0x%x ", 686 up.config.Description, 687 nodeLocal.Desc(), 688 nodeUp.Hash, nodeLocal.Hash) 689 690 // first compare node points 691 // key in below map is the index of the point in the upstream node 692 upstreamProcessed := make(map[int]bool) 693 694 for _, p := range nodeLocal.Points { 695 found := false 696 for i, pUp := range nodeUp.Points { 697 if p.IsMatch(pUp.Type, pUp.Key) { 698 found = true 699 upstreamProcessed[i] = true 700 if p.Time.After(pUp.Time) { 701 // need to send point upstream 702 err := SendNodePoint(up.ncRemote, nodeUp.ID, p, true) 703 if err != nil { 704 log.Println("Error syncing point upstream:", err) 705 } 706 } else if p.Time.Before(pUp.Time) { 707 // need to update point locally 708 err := SendNodePoint(up.nc, nodeLocal.ID, pUp, true) 709 if err != nil { 710 log.Println("Error syncing point from upstream:", err) 711 } 712 } 713 } 714 } 715 716 if !found { 717 err := SendNodePoint(up.ncRemote, nodeUp.ID, p, true) 718 if err != nil { 719 log.Println("Error sending point:", err) 720 } 721 } 722 } 723 724 // check for any points that do not exist locally 725 for i, pUp := range nodeUp.Points { 726 if _, ok := upstreamProcessed[i]; !ok { 727 err := SendNodePoint(up.nc, nodeLocal.ID, pUp, true) 728 if err != nil { 729 log.Println("Error syncing point from upstream:", err) 730 } 731 } 732 } 733 734 // now compare edge points 735 // key in below map is the index of the point in the upstream node 736 upstreamProcessed = make(map[int]bool) 737 738 // only check edge points if we are not the root node 739 if nodeLocal.ID != up.rootLocal.ID { 740 for _, p := range nodeLocal.EdgePoints { 741 found := false 742 for i, pUp := range nodeUp.EdgePoints { 743 if p.IsMatch(pUp.Type, pUp.Key) { 744 found = true 745 upstreamProcessed[i] = true 746 if p.Time.After(pUp.Time) { 747 // need to send point upstream 748 err := SendEdgePoint(up.ncRemote, nodeUp.ID, nodeUp.Parent, p, true) 749 if err != nil { 750 log.Println("Error syncing point upstream:", err) 751 } 752 } else if p.Time.Before(pUp.Time) { 753 // need to update point locally 754 err := SendEdgePoint(up.nc, nodeLocal.ID, nodeLocal.Parent, pUp, true) 755 if err != nil { 756 log.Println("Error syncing point from upstream:", err) 757 } 758 } 759 } 760 } 761 762 if !found { 763 err := SendEdgePoint(up.ncRemote, nodeUp.ID, nodeUp.Parent, p, true) 764 if err != nil { 765 log.Println("Error sending point:", err) 766 } 767 } 768 } 769 770 // check for any points that do not exist locally 771 for i, pUp := range nodeUp.EdgePoints { 772 if _, ok := upstreamProcessed[i]; !ok { 773 err := SendEdgePoint(up.nc, nodeLocal.ID, nodeLocal.Parent, pUp, true) 774 if err != nil { 775 log.Println("Error syncing edge point from upstream:", err) 776 } 777 } 778 } 779 } 780 781 // sync child nodes 782 children, err := GetNodes(up.ncLocal, nodeLocal.ID, "all", "", false) 783 if err != nil { 784 return fmt.Errorf("Error getting local node children: %v", err) 785 } 786 787 // FIXME optimization we get the edges here and not the full child node 788 upChildren, err := GetNodes(up.ncRemote, nodeUp.ID, "all", "", false) 789 if err != nil { 790 return fmt.Errorf("Error getting upstream node children: %v", err) 791 } 792 793 // map index is index of upChildren 794 upChildProcessed := make(map[int]bool) 795 796 for _, child := range children { 797 found := false 798 for i, upChild := range upChildren { 799 if child.ID == upChild.ID { 800 found = true 801 upChildProcessed[i] = true 802 if child.Hash != upChild.Hash { 803 err := up.syncNode(nodeLocal.ID, child.ID) 804 if err != nil { 805 fmt.Println("Error syncing node: ", err) 806 } 807 } 808 } 809 } 810 811 if !found { 812 // need to send node upstream 813 err := up.sendNodesRemote(child) 814 if err != nil { 815 log.Println("Error sending node upstream:", err) 816 } 817 818 err = up.subscribeRemoteNode(child.Parent, child.ID) 819 if err != nil { 820 log.Println("Error subscribing to upstream:", err) 821 } 822 } 823 } 824 825 for i, upChild := range upChildren { 826 if _, ok := upChildProcessed[i]; !ok { 827 err := up.sendNodesLocal(upChild) 828 if err != nil { 829 log.Println("Error getting node from upstream:", err) 830 } 831 err = up.subscribeRemoteNode(upChild.Parent, upChild.ID) 832 if err != nil { 833 log.Println("Error subscribing to upstream:", err) 834 } 835 } 836 } 837 838 return nil 839 }