github.com/braveheart12/just@v0.8.7/ledger/artifactmanager/jettreeupdater.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package artifactmanager
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  
    23  	"github.com/insolar/insolar"
    24  	"github.com/insolar/insolar/ledger/storage/nodes"
    25  	"github.com/pkg/errors"
    26  
    27  	"github.com/insolar/insolar/core"
    28  	"github.com/insolar/insolar/core/message"
    29  	"github.com/insolar/insolar/core/reply"
    30  	"github.com/insolar/insolar/instrumentation/inslogger"
    31  	"github.com/insolar/insolar/instrumentation/instracer"
    32  	"github.com/insolar/insolar/ledger/storage"
    33  	"github.com/insolar/insolar/ledger/storage/jet"
    34  )
    35  
    36  type seqEntry struct {
    37  	ch   chan struct{}
    38  	once sync.Once
    39  }
    40  
    41  type seqKey struct {
    42  	pulse core.PulseNumber
    43  	jet   core.RecordID
    44  }
    45  
    46  type fetchResult struct {
    47  	jet *core.RecordID
    48  	err error
    49  }
    50  
    51  type jetTreeUpdater struct {
    52  	Nodes          nodes.Accessor
    53  	JetStorage     storage.JetStorage
    54  	MessageBus     core.MessageBus
    55  	JetCoordinator core.JetCoordinator
    56  
    57  	seqMutex  sync.Mutex
    58  	sequencer map[seqKey]*seqEntry
    59  }
    60  
    61  func newJetTreeUpdater(
    62  	ans nodes.Accessor,
    63  	js storage.JetStorage, mb core.MessageBus, jc core.JetCoordinator,
    64  ) *jetTreeUpdater {
    65  	return &jetTreeUpdater{
    66  		Nodes:          ans,
    67  		JetStorage:     js,
    68  		MessageBus:     mb,
    69  		JetCoordinator: jc,
    70  		sequencer:      map[seqKey]*seqEntry{},
    71  	}
    72  }
    73  
    74  func (jtu *jetTreeUpdater) fetchJet(
    75  	ctx context.Context, target core.RecordID, pulse core.PulseNumber,
    76  ) (*core.RecordID, error) {
    77  	ctx, span := instracer.StartSpan(ctx, "jet_tree_updater.fetch_jet")
    78  	defer span.End()
    79  
    80  	// Look in the local tree. Return if the actual jet found.
    81  	jetID, actual := jtu.JetStorage.FindJet(ctx, pulse, target)
    82  	if actual {
    83  		return jetID, nil
    84  	}
    85  
    86  	// Not actual in our tree, asking neighbors for jet.
    87  	span.Annotate(nil, "tree in DB is not actual")
    88  	key := seqKey{pulse, *jetID}
    89  
    90  	executing := false
    91  
    92  	jtu.seqMutex.Lock()
    93  	if _, ok := jtu.sequencer[key]; !ok {
    94  		jtu.sequencer[key] = &seqEntry{ch: make(chan struct{})}
    95  		executing = true
    96  	}
    97  	entry := jtu.sequencer[key]
    98  	jtu.seqMutex.Unlock()
    99  
   100  	span.Annotate(nil, "got sequencer entry")
   101  
   102  	if !executing {
   103  		<-entry.ch
   104  
   105  		// Tree was updated in another thread, rechecking.
   106  		span.Annotate(nil, "somebody else updated actuality")
   107  		return jtu.fetchJet(ctx, target, pulse)
   108  	}
   109  
   110  	defer func() {
   111  		entry.once.Do(func() {
   112  			close(entry.ch)
   113  		})
   114  
   115  		jtu.seqMutex.Lock()
   116  		delete(jtu.sequencer, key)
   117  		jtu.seqMutex.Unlock()
   118  	}()
   119  
   120  	resJet, err := jtu.fetchActualJetFromOtherNodes(ctx, target, pulse)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	jtu.JetStorage.UpdateJetTree(ctx, pulse, true, *resJet)
   126  
   127  	return resJet, nil
   128  }
   129  
   130  func (jtu *jetTreeUpdater) releaseJet(ctx context.Context, jetID core.RecordID, pulse core.PulseNumber) {
   131  	jtu.seqMutex.Lock()
   132  	defer jtu.seqMutex.Unlock()
   133  
   134  	depth, _ := jet.Jet(jetID)
   135  	for {
   136  		key := seqKey{pulse, jetID}
   137  		if v, ok := jtu.sequencer[key]; ok {
   138  			v.once.Do(func() {
   139  				close(v.ch)
   140  			})
   141  
   142  			delete(jtu.sequencer, key)
   143  		}
   144  
   145  		if depth == 0 {
   146  			break
   147  		}
   148  		jetID = jet.Parent(jetID)
   149  		depth--
   150  	}
   151  }
   152  
   153  func (jtu *jetTreeUpdater) fetchActualJetFromOtherNodes(
   154  	ctx context.Context, target core.RecordID, pulse core.PulseNumber,
   155  ) (*core.RecordID, error) {
   156  	ctx, span := instracer.StartSpan(ctx, "jet_tree_updater.fetch_jet_from_other_nodes")
   157  	defer span.End()
   158  
   159  	ch := make(chan fetchResult, 1)
   160  
   161  	go func() {
   162  		nodes, err := jtu.otherNodesForPulse(ctx, pulse)
   163  		if err != nil {
   164  			ch <- fetchResult{nil, err}
   165  			return
   166  		}
   167  
   168  		num := len(nodes)
   169  
   170  		wg := sync.WaitGroup{}
   171  		wg.Add(num)
   172  
   173  		once := sync.Once{}
   174  
   175  		replies := make([]*reply.Jet, num)
   176  		for i, node := range nodes {
   177  			go func(i int, node insolar.Node) {
   178  				ctx, span := instracer.StartSpan(ctx, "jet_tree_updater.one_node_get_jet")
   179  				defer span.End()
   180  
   181  				defer wg.Done()
   182  
   183  				nodeID := node.ID
   184  				rep, err := jtu.MessageBus.Send(
   185  					ctx,
   186  					&message.GetJet{Object: target, Pulse: pulse},
   187  					&core.MessageSendOptions{Receiver: &nodeID},
   188  				)
   189  				if err != nil {
   190  					inslogger.FromContext(ctx).Error(
   191  						errors.Wrap(err, "couldn't get jet"),
   192  					)
   193  					return
   194  				}
   195  
   196  				r, ok := rep.(*reply.Jet)
   197  				if !ok {
   198  					inslogger.FromContext(ctx).Errorf("middleware.fetchActualJetFromOtherNodes: unexpected reply: %#v\n", rep)
   199  					return
   200  				}
   201  
   202  				if !r.Actual {
   203  					return
   204  				}
   205  
   206  				once.Do(func() {
   207  					jetID := r.ID
   208  					ch <- fetchResult{&jetID, nil}
   209  					close(ch)
   210  				})
   211  
   212  				replies[i] = r
   213  			}(i, node)
   214  		}
   215  		wg.Wait()
   216  
   217  		seen := make(map[core.RecordID]struct{})
   218  		res := make([]*core.RecordID, 0)
   219  		for _, r := range replies {
   220  			if r == nil {
   221  				continue
   222  			}
   223  			if _, ok := seen[r.ID]; ok {
   224  				continue
   225  			}
   226  
   227  			seen[r.ID] = struct{}{}
   228  			res = append(res, &r.ID)
   229  		}
   230  
   231  		if len(res) == 0 {
   232  			inslogger.FromContext(ctx).WithFields(map[string]interface{}{
   233  				"pulse":  pulse,
   234  				"object": target.DebugString(),
   235  			}).Error("all lights for pulse have no actual jet for object")
   236  			ch <- fetchResult{nil, errors.New("impossible situation")}
   237  			close(ch)
   238  		} else if len(res) > 1 {
   239  			inslogger.FromContext(ctx).WithFields(map[string]interface{}{
   240  				"pulse":  pulse,
   241  				"object": target.DebugString(),
   242  			}).Error("lights said different actual jet for object")
   243  		}
   244  	}()
   245  
   246  	res := <-ch
   247  	return res.jet, res.err
   248  }
   249  
   250  func (jtu *jetTreeUpdater) otherNodesForPulse(
   251  	ctx context.Context, pulse core.PulseNumber,
   252  ) ([]insolar.Node, error) {
   253  	ctx, span := instracer.StartSpan(ctx, "jet_tree_updater.other_nodes_for_pulse")
   254  	defer span.End()
   255  
   256  	res, err := jtu.Nodes.InRole(pulse, core.StaticRoleLightMaterial)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	me := jtu.JetCoordinator.Me()
   262  	for i := range res {
   263  		if res[i].ID == me {
   264  			res = append(res[:i], res[i+1:]...)
   265  			break
   266  		}
   267  	}
   268  
   269  	num := len(res)
   270  	if num == 0 {
   271  		inslogger.FromContext(ctx).Error(
   272  			"This shouldn't happen. We're solo active light material",
   273  		)
   274  
   275  		return nil, errors.New("impossible situation")
   276  	}
   277  
   278  	return res, nil
   279  }