github.com/ergo-services/ergo@v1.999.224/gen/stage.go (about)

     1  package gen
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/ergo-services/ergo/etf"
     8  	"github.com/ergo-services/ergo/lib"
     9  	//"github.com/ergo-services/ergo/lib"
    10  )
    11  
    12  type StageCancelMode uint
    13  
    14  // StageOptions defines the producer configuration using Init callback. It will be ignored
    15  // if it acts as a consumer only.
    16  type StageOptions struct {
    17  
    18  	// DisableDemandHandle. the demand is always handling using the HandleDemand callback.
    19  	// When this options is set to 'true', demands are accumulated until mode is
    20  	// set back to 'false' using SetDemandHandle(true) method
    21  	DisableDemandHandle bool
    22  
    23  	// BufferSize the size of the buffer to store events without demand.
    24  	// default value = defaultDispatcherBufferSize
    25  	BufferSize uint
    26  
    27  	// BufferKeepLast defines whether the first or last entries should be
    28  	// kept on the buffer in case the buffer size is exceeded.
    29  	BufferKeepLast bool
    30  
    31  	Dispatcher StageDispatcherBehavior
    32  }
    33  
    34  type StageStatus error
    35  
    36  const (
    37  	StageCancelPermanent StageCancelMode = 0
    38  	StageCancelTransient StageCancelMode = 1
    39  	StageCancelTemporary StageCancelMode = 2
    40  
    41  	defaultDispatcherBufferSize = 10000
    42  )
    43  
    44  var (
    45  	StageStatusOK           StageStatus = nil
    46  	StageStatusStop         StageStatus = fmt.Errorf("stop")
    47  	StageStatusUnsupported  StageStatus = fmt.Errorf("unsupported")
    48  	StageStatusNotAProducer StageStatus = fmt.Errorf("not a producer")
    49  )
    50  
    51  // StageBehavior interface for the Stage inmplementation
    52  type StageBehavior interface {
    53  	ServerBehavior
    54  
    55  	// InitStage
    56  	InitStage(process *StageProcess, args ...etf.Term) (StageOptions, error)
    57  
    58  	// HandleDemand this callback is invoked on a producer stage
    59  	// The producer that implements this callback must either store the demand, or return the amount of requested events.
    60  	HandleDemand(process *StageProcess, subscription StageSubscription, count uint) (etf.List, StageStatus)
    61  
    62  	// HandleEvents this callback is invoked on a consumer stage.
    63  	HandleEvents(process *StageProcess, subscription StageSubscription, events etf.List) StageStatus
    64  
    65  	// HandleSubscribe This callback is invoked on a producer stage.
    66  	HandleSubscribe(process *StageProcess, subscription StageSubscription, options StageSubscribeOptions) StageStatus
    67  
    68  	// HandleSubscribed this callback is invoked as a confirmation for the subscription request
    69  	// Returning false means that demand must be sent to producers explicitly using Ask method.
    70  	// Returning true means the stage implementation will take care of automatically sending.
    71  	HandleSubscribed(process *StageProcess, subscription StageSubscription, opts StageSubscribeOptions) (bool, StageStatus)
    72  
    73  	// HandleCancel
    74  	// Invoked when a consumer is no longer subscribed to a producer (invoked on a producer stage)
    75  	// The cancelReason will be a {Cancel: "cancel", Reason: _} if the reason for cancellation
    76  	// was a Stage.Cancel call. Any other value means the cancellation reason was
    77  	// due to an EXIT.
    78  	HandleCancel(process *StageProcess, subscription StageSubscription, reason string) StageStatus
    79  
    80  	// HandleCanceled
    81  	// Invoked when a consumer is no longer subscribed to a producer (invoked on a consumer stage)
    82  	// Termination this stage depends on a cancel mode for the given subscription. For the cancel mode
    83  	// StageCancelPermanent - this stage will be terminated right after this callback invoking.
    84  	// For the cancel mode StageCancelTransient - it depends on a reason of subscription canceling.
    85  	// Cancel mode StageCancelTemporary keeps this stage alive whether the reason could be.
    86  	HandleCanceled(process *StageProcess, subscription StageSubscription, reason string) StageStatus
    87  
    88  	// HandleStageCall this callback is invoked on ServerProcess.Call. This method is optional
    89  	// for the implementation
    90  	HandleStageCall(process *StageProcess, from ServerFrom, message etf.Term) (etf.Term, ServerStatus)
    91  	// HandleStageDirect this callback is invoked on Process.Direct. This method is optional
    92  	// for the implementation
    93  	HandleStageDirect(process *StageProcess, ref etf.Ref, message interface{}) (interface{}, DirectStatus)
    94  	// HandleStageCast this callback is invoked on ServerProcess.Cast. This method is optional
    95  	// for the implementation
    96  	HandleStageCast(process *StageProcess, message etf.Term) ServerStatus
    97  	// HandleStageInfo this callback is invoked on Process.Send. This method is optional
    98  	// for the implementation
    99  	HandleStageInfo(process *StageProcess, message etf.Term) ServerStatus
   100  	// HandleStageTerminate this callback is invoked on a termination process
   101  	HandleStageTerminate(process *StageProcess, reason string)
   102  }
   103  
   104  type StageSubscription struct {
   105  	Pid etf.Pid
   106  	ID  etf.Ref
   107  }
   108  
   109  type subscriptionInternal struct {
   110  	Producer     etf.Term
   111  	Subscription StageSubscription
   112  	options      StageSubscribeOptions
   113  	Monitor      etf.Ref
   114  	// number of event requests (demands) made as a consumer.
   115  	count uint
   116  }
   117  
   118  type StageSubscribeOptions struct {
   119  	MinDemand uint `etf:"min_demand"`
   120  	MaxDemand uint `etf:"max_demand"`
   121  	// The stage implementation will take care of automatically sending
   122  	// demand to producer (as a default behavior). You can disable it
   123  	// setting ManualDemand to true
   124  	ManualDemand bool `etf:"manual"`
   125  	// What should happened with consumer if producer has terminated
   126  	// StageCancelPermanent the consumer exits when the producer cancels or exits.
   127  	// StageCancelTransient the consumer exits only if reason is not "normal",
   128  	// "shutdown", or {"shutdown", _}
   129  	// StageCancelTemporary the consumer never exits
   130  	Cancel StageCancelMode `etf:"cancel"`
   131  
   132  	// Partition is defined the number of partition this subscription should belongs to.
   133  	// This option uses in the DispatcherPartition
   134  	Partition uint `etf:"partition"`
   135  
   136  	// Extra is intended to be a custom set of options for the custom implementation
   137  	// of StageDispatcherBehavior
   138  	Extra etf.Term `etf:"extra"`
   139  }
   140  
   141  type StageCancelReason struct {
   142  	Cancel string
   143  	Reason string
   144  }
   145  
   146  type Stage struct {
   147  	Server
   148  }
   149  
   150  type StageProcess struct {
   151  	ServerProcess
   152  
   153  	options         StageOptions
   154  	demandBuffer    []demandRequest
   155  	dispatcherState interface{}
   156  	// keep our subscriptions
   157  	producers map[etf.Ref]*subscriptionInternal
   158  	// keep our subscribers
   159  	consumers map[etf.Pid]*subscriptionInternal
   160  	//
   161  	behavior StageBehavior
   162  }
   163  
   164  type stageRequestCommand struct {
   165  	Cmd  etf.Atom
   166  	Opt1 interface{}
   167  	Opt2 interface{}
   168  }
   169  
   170  type stageMessage struct {
   171  	Request      etf.Atom
   172  	Subscription StageSubscription
   173  	Command      interface{}
   174  }
   175  
   176  type setManualDemand struct {
   177  	subscription StageSubscription
   178  	enable       bool
   179  }
   180  
   181  type setCancelMode struct {
   182  	subscription StageSubscription
   183  	cancel       StageCancelMode
   184  }
   185  
   186  type setForwardDemand struct {
   187  	forward bool
   188  }
   189  
   190  type demandRequest struct {
   191  	subscription StageSubscription
   192  	count        uint
   193  }
   194  
   195  // SetCancelMode defines how consumer will handle termination of the producer. There are 3 modes:
   196  // StageCancelPermanent (default) - consumer exits when the producer cancels or exits
   197  // StageCancelTransient - consumer exits only if reason is not normal, shutdown, or {shutdown, reason}
   198  // StageCancelTemporary - never exits
   199  func (s *Stage) SetCancelMode(p Process, subscription StageSubscription, cancel StageCancelMode) error {
   200  	message := setCancelMode{
   201  		subscription: subscription,
   202  		cancel:       cancel,
   203  	}
   204  
   205  	_, err := p.Direct(message)
   206  	return err
   207  }
   208  
   209  //
   210  // StageProcess methods
   211  //
   212  
   213  // SetAutoDemand setting this option to false means that demand must be sent to producers
   214  // explicitly using Ask method. This mode can be used when a special behavior is desired.
   215  // Setting this options to true enables auto demand mode (this is default mode for the consumer)
   216  func (p *StageProcess) SetAutoDemand(subscription StageSubscription, autodemand bool) error {
   217  	subInternal, ok := p.producers[subscription.ID]
   218  	if !ok {
   219  		return fmt.Errorf("unknown subscription")
   220  	}
   221  	subInternal.options.ManualDemand = autodemand == false
   222  	if subInternal.count < subInternal.options.MinDemand && subInternal.options.ManualDemand == false {
   223  		cnt := subInternal.options.MaxDemand - subInternal.count
   224  		p.sendDemand(subInternal.Producer, subscription, cnt)
   225  		subInternal.count += cnt
   226  	}
   227  	return nil
   228  }
   229  
   230  // AutoDemand returns value of the auto demand option
   231  func (p *StageProcess) AutoDemand(subscription StageSubscription) (bool, error) {
   232  	subInternal, ok := p.producers[subscription.ID]
   233  	if !ok {
   234  		return false, fmt.Errorf("unknown subscription")
   235  	}
   236  	return subInternal.options.ManualDemand == false, nil
   237  }
   238  
   239  // SetDemandHandle setting this option to false disables handling demand requests on a producer stage.
   240  // This is useful as a synchronization mechanism, where the demand is accumulated until
   241  // all consumers are subscribed. By default this option is true.
   242  func (p *StageProcess) SetDemandHandle(enable bool) {
   243  	p.options.DisableDemandHandle = enable == false
   244  	if enable == true {
   245  		// create demand with count = 0, which will be ignored but start
   246  		// the processing of the buffered demands
   247  		msg := etf.Tuple{
   248  			etf.Atom("$gen_producer"),
   249  			etf.Tuple{etf.Pid{}, etf.Ref{}},
   250  			etf.Tuple{etf.Atom("ask"), 0},
   251  		}
   252  		p.Send(p.Self(), msg)
   253  	}
   254  }
   255  
   256  // DemandHandle returns whether enabled handling demand requests.
   257  func (p *StageProcess) DemandHandle() bool {
   258  	return p.options.DisableDemandHandle == false
   259  }
   260  
   261  // SetCancelMode defines how consumer will handle termination of the producer. There are 3 modes:
   262  // StageCancelPermanent (default) - consumer exits when the producer cancels or exits
   263  // StageCancelTransient - consumer exits only if reason is not normal, shutdown, or {shutdown, reason}
   264  // StageCancelTemporary - never exits
   265  func (p *StageProcess) SetCancelMode(subscription StageSubscription, mode StageCancelMode) error {
   266  	subInternal, ok := p.producers[subscription.ID]
   267  	if !ok {
   268  		return fmt.Errorf("unknown subscription")
   269  	}
   270  
   271  	subInternal.options.Cancel = mode
   272  	return nil
   273  }
   274  
   275  // CancelMode returns current cancel mode for the consumer
   276  func (p *StageProcess) CancelMode(subscription StageSubscription) (StageCancelMode, error) {
   277  	subInternal, ok := p.producers[subscription.ID]
   278  	if !ok {
   279  		return 0, fmt.Errorf("unknown subscription")
   280  	}
   281  
   282  	return subInternal.options.Cancel, nil
   283  }
   284  
   285  // Subscribe subscribes to the given producer. HandleSubscribed callback will be invoked
   286  // on a consumer stage once a request for the subscription is sent. If something went wrong
   287  // on a producer side the callback HandleCancel will be invoked with a reason of cancelation.
   288  func (p *StageProcess) Subscribe(producer etf.Term, opts StageSubscribeOptions) (StageSubscription, error) {
   289  	var subscription StageSubscription
   290  	switch producer.(type) {
   291  	case string:
   292  	case etf.Pid:
   293  	case ProcessID:
   294  	default:
   295  		return subscription, fmt.Errorf("allowed type for producer: etf.Pid, string, gen.ProcessID")
   296  	}
   297  
   298  	subscription_id := p.MonitorProcess(producer)
   299  	subscription.Pid = p.Self()
   300  	subscription.ID = subscription_id
   301  
   302  	subscribe_opts := etf.List{
   303  		etf.Tuple{
   304  			etf.Atom("min_demand"),
   305  			opts.MinDemand,
   306  		},
   307  		etf.Tuple{
   308  			etf.Atom("max_demand"),
   309  			opts.MaxDemand,
   310  		},
   311  		etf.Tuple{
   312  			etf.Atom("cancel"),
   313  			int(opts.Cancel), // custom types couldn't be handled by etf.Encode
   314  		},
   315  		etf.Tuple{
   316  			etf.Atom("manual"),
   317  			opts.ManualDemand,
   318  		},
   319  		etf.Tuple{
   320  			etf.Atom("partition"),
   321  			opts.Partition,
   322  		},
   323  	}
   324  
   325  	// In order to get rid of race condition we should send this message
   326  	// before we send 'subscribe' to the producer process. Just
   327  	// to make sure if we registered this subscription before the MessageDown
   328  	// or MessageExit message arrived in case of something went wrong.
   329  	msg := etf.Tuple{
   330  		etf.Atom("$gen_consumer"),
   331  		etf.Tuple{p.Self(), subscription_id},
   332  		etf.Tuple{etf.Atom("subscribed"), producer, subscribe_opts},
   333  	}
   334  	p.Send(p.Self(), msg)
   335  
   336  	msg = etf.Tuple{
   337  		etf.Atom("$gen_producer"),
   338  		etf.Tuple{p.Self(), subscription_id},
   339  		etf.Tuple{etf.Atom("subscribe"), etf.Atom("nil"), subscribe_opts},
   340  	}
   341  	p.Send(producer, msg)
   342  
   343  	return subscription, nil
   344  }
   345  
   346  // SendEvents sends events to the subscribers
   347  func (p *StageProcess) SendEvents(events etf.List) error {
   348  	var deliver []StageDispatchItem
   349  	// dispatch to the subscribers
   350  	if len(p.consumers) == 0 {
   351  		return fmt.Errorf("no subscribers")
   352  	}
   353  	deliver = p.options.Dispatcher.Dispatch(p.dispatcherState, events)
   354  	if len(deliver) == 0 {
   355  		return fmt.Errorf("no demand")
   356  	}
   357  	for d := range deliver {
   358  		msg := etf.Tuple{
   359  			etf.Atom("$gen_consumer"),
   360  			etf.Tuple{deliver[d].subscription.Pid, deliver[d].subscription.ID},
   361  			deliver[d].events,
   362  		}
   363  		p.Send(deliver[d].subscription.Pid, msg)
   364  	}
   365  	return nil
   366  }
   367  
   368  // Ask makes a demand request for the given subscription. This function must only be
   369  // used in the cases when a consumer sets a subscription to manual mode using DisableAutoDemand
   370  func (p *StageProcess) Ask(subscription StageSubscription, count uint) error {
   371  	subInternal, ok := p.producers[subscription.ID]
   372  	if ok == false {
   373  		return fmt.Errorf("unknown subscription")
   374  	}
   375  	if subInternal.options.ManualDemand == false {
   376  		return fmt.Errorf("auto demand")
   377  	}
   378  
   379  	p.sendDemand(subInternal.Producer, subInternal.Subscription, count)
   380  	subInternal.count += count
   381  	return nil
   382  }
   383  
   384  // Cancel
   385  func (p *StageProcess) Cancel(subscription StageSubscription, reason string) error {
   386  	// if we act as a consumer with this subscription
   387  	if subInternal, ok := p.producers[subscription.ID]; ok {
   388  		msg := etf.Tuple{
   389  			etf.Atom("$gen_producer"),
   390  			etf.Tuple{subscription.Pid, subscription.ID},
   391  			etf.Tuple{etf.Atom("cancel"), reason},
   392  		}
   393  		p.Send(subInternal.Producer, msg)
   394  		cmd := stageRequestCommand{
   395  			Cmd:  etf.Atom("cancel"),
   396  			Opt1: "normal",
   397  		}
   398  		if _, err := p.handleConsumer(subInternal.Subscription, cmd); err != nil {
   399  			return err
   400  		}
   401  		return nil
   402  	}
   403  	// if we act as a producer within this subscription
   404  	if subInternal, ok := p.consumers[subscription.Pid]; ok {
   405  		msg := etf.Tuple{
   406  			etf.Atom("$gen_consumer"),
   407  			etf.Tuple{subscription.Pid, subscription.ID},
   408  			etf.Tuple{etf.Atom("cancel"), reason},
   409  		}
   410  		p.Send(subscription.Pid, msg)
   411  		p.DemonitorProcess(subInternal.Monitor)
   412  		cmd := stageRequestCommand{
   413  			Cmd:  etf.Atom("cancel"),
   414  			Opt1: "normal",
   415  		}
   416  		if _, err := p.handleProducer(subInternal.Subscription, cmd); err != nil {
   417  			return err
   418  		}
   419  		return nil
   420  	}
   421  	return fmt.Errorf("unknown subscription")
   422  
   423  }
   424  
   425  // gen.Server callbacks
   426  func (gst *Stage) Init(process *ServerProcess, args ...etf.Term) error {
   427  	stageProcess := &StageProcess{
   428  		ServerProcess: *process,
   429  		producers:     make(map[etf.Ref]*subscriptionInternal),
   430  		consumers:     make(map[etf.Pid]*subscriptionInternal),
   431  	}
   432  	// do not inherit parent State
   433  	stageProcess.State = nil
   434  
   435  	behavior := process.Behavior().(StageBehavior)
   436  	behavior, ok := process.Behavior().(StageBehavior)
   437  	if !ok {
   438  		return fmt.Errorf("Stage: not a StageBehavior")
   439  	}
   440  	stageProcess.behavior = behavior
   441  
   442  	stageOpts, err := behavior.InitStage(stageProcess, args...)
   443  	if err != nil {
   444  		return err
   445  	}
   446  
   447  	if stageOpts.BufferSize == 0 {
   448  		stageOpts.BufferSize = defaultDispatcherBufferSize
   449  	}
   450  
   451  	// if dispatcher wasn't specified create a default one StageDispatcherDemand
   452  	if stageOpts.Dispatcher == nil {
   453  		stageOpts.Dispatcher = CreateStageDispatcherDemand()
   454  	}
   455  
   456  	stageProcess.dispatcherState = stageOpts.Dispatcher.Init(stageOpts)
   457  	stageProcess.options = stageOpts
   458  
   459  	process.State = stageProcess
   460  	return nil
   461  }
   462  
   463  func (gst *Stage) HandleCall(process *ServerProcess, from ServerFrom, message etf.Term) (etf.Term, ServerStatus) {
   464  	stageProcess := process.State.(*StageProcess)
   465  	return stageProcess.behavior.HandleStageCall(stageProcess, from, message)
   466  }
   467  
   468  func (gst *Stage) HandleDirect(process *ServerProcess, ref etf.Ref, message interface{}) (interface{}, DirectStatus) {
   469  	stageProcess := process.State.(*StageProcess)
   470  	return stageProcess.behavior.HandleStageDirect(stageProcess, ref, message)
   471  }
   472  
   473  func (gst *Stage) HandleCast(process *ServerProcess, message etf.Term) ServerStatus {
   474  	stageProcess := process.State.(*StageProcess)
   475  	return stageProcess.behavior.HandleStageCast(stageProcess, message)
   476  }
   477  
   478  func (gst *Stage) HandleInfo(process *ServerProcess, message etf.Term) ServerStatus {
   479  	var r stageMessage
   480  
   481  	stageProcess := process.State.(*StageProcess)
   482  
   483  	// check if we got a MessageDown
   484  	if d, isDown := IsMessageDown(message); isDown {
   485  		if err := stageProcess.handleStageDown(d); err != nil {
   486  			return err
   487  		}
   488  		return ServerStatusOK
   489  	}
   490  
   491  	if err := etf.TermIntoStruct(message, &r); err != nil {
   492  		reply := stageProcess.behavior.HandleStageInfo(stageProcess, message)
   493  		return reply
   494  	}
   495  
   496  	_, err := stageProcess.handleStageRequest(r)
   497  
   498  	switch err {
   499  	case nil:
   500  		return ServerStatusOK
   501  	case StageStatusStop:
   502  		return ServerStatusStop
   503  	case StageStatusUnsupported:
   504  		status := stageProcess.behavior.HandleStageInfo(stageProcess, message)
   505  		return status
   506  	default:
   507  		return err
   508  	}
   509  }
   510  
   511  func (gst *Stage) Terminate(process *ServerProcess, reason string) {
   512  	stageProcess := process.State.(*StageProcess)
   513  	stageProcess.behavior.HandleStageTerminate(stageProcess, reason)
   514  }
   515  
   516  // default callbacks
   517  
   518  // InitStage
   519  func (gst *Stage) InitStage(process *StageProcess, args ...etf.Term) error {
   520  	return nil
   521  }
   522  
   523  // HandleSagaCall
   524  func (gst *Stage) HandleStageCall(process *StageProcess, from ServerFrom, message etf.Term) (etf.Term, ServerStatus) {
   525  	// default callback if it wasn't implemented
   526  	lib.Warning("HandleStageCall: unhandled message (from %#v) %#v", from, message)
   527  	return etf.Atom("ok"), ServerStatusOK
   528  }
   529  
   530  // HandleStageDirect
   531  func (gst *Stage) HandleStageDirect(process *StageProcess, ref etf.Ref, message interface{}) (interface{}, DirectStatus) {
   532  	// default callback if it wasn't implemented
   533  	return nil, lib.ErrUnsupportedRequest
   534  }
   535  
   536  // HandleStageCast
   537  func (gst *Stage) HandleStageCast(process *StageProcess, message etf.Term) ServerStatus {
   538  	// default callback if it wasn't implemented
   539  	lib.Warning("HandleStageCast: unhandled message %#v", message)
   540  	return ServerStatusOK
   541  }
   542  
   543  // HandleStageInfo
   544  func (gst *Stage) HandleStageInfo(process *StageProcess, message etf.Term) ServerStatus {
   545  	// default callback if it wasn't implemnted
   546  	lib.Warning("HandleStageInfo: unhandled message %#v", message)
   547  	return ServerStatusOK
   548  }
   549  
   550  func (gst *Stage) HandleStageTerminate(process *StageProcess, reason string) {
   551  	return
   552  }
   553  
   554  // HandleSubscribe
   555  func (gst *Stage) HandleSubscribe(process *StageProcess, subscription StageSubscription, options StageSubscribeOptions) StageStatus {
   556  	return StageStatusNotAProducer
   557  }
   558  
   559  // HandleSubscribed
   560  func (gst *Stage) HandleSubscribed(process *StageProcess, subscription StageSubscription, opts StageSubscribeOptions) (bool, StageStatus) {
   561  	return opts.ManualDemand, StageStatusOK
   562  }
   563  
   564  // HandleCancel
   565  func (gst *Stage) HandleCancel(process *StageProcess, subscription StageSubscription, reason string) StageStatus {
   566  	// default callback if it wasn't implemented
   567  	return StageStatusOK
   568  }
   569  
   570  // HandleCanceled
   571  func (gst *Stage) HandleCanceled(process *StageProcess, subscription StageSubscription, reason string) StageStatus {
   572  	// default callback if it wasn't implemented
   573  	return StageStatusOK
   574  }
   575  
   576  // HanndleEvents
   577  func (gst *Stage) HandleEvents(process *StageProcess, subscription StageSubscription, events etf.List) StageStatus {
   578  	lib.Warning("Stage HandleEvents: unhandled subscription (%#v) events %#v", subscription, events)
   579  	return StageStatusOK
   580  }
   581  
   582  // HandleDemand
   583  func (gst *Stage) HandleDemand(process *StageProcess, subscription StageSubscription, count uint) (etf.List, StageStatus) {
   584  	lib.Warning("Stage HandleDemand: unhandled subscription (%#v) demand %#v", subscription, count)
   585  	return nil, StageStatusOK
   586  }
   587  
   588  // private functions
   589  
   590  func (p *StageProcess) handleStageRequest(m stageMessage) (etf.Term, StageStatus) {
   591  	var command stageRequestCommand
   592  	switch m.Request {
   593  	case "$gen_consumer":
   594  		// I wish i had {events, [...]} for the events message (in
   595  		// fashion of the other messages), but the original autors
   596  		// made this way, so i have to use this little hack in order
   597  		// to use the same handler
   598  		if cmd, ok := m.Command.(etf.List); ok {
   599  			command.Cmd = etf.Atom("events")
   600  			command.Opt1 = cmd
   601  			return p.handleConsumer(m.Subscription, command)
   602  		}
   603  		if err := etf.TermIntoStruct(m.Command, &command); err != nil {
   604  			return nil, StageStatusUnsupported
   605  		}
   606  		return p.handleConsumer(m.Subscription, command)
   607  	case "$gen_producer":
   608  		if err := etf.TermIntoStruct(m.Command, &command); err != nil {
   609  			return nil, StageStatusUnsupported
   610  		}
   611  		return p.handleProducer(m.Subscription, command)
   612  	}
   613  	return nil, StageStatusUnsupported
   614  }
   615  
   616  func (p *StageProcess) handleConsumer(subscription StageSubscription, cmd stageRequestCommand) (etf.Term, error) {
   617  	var subscriptionOpts StageSubscribeOptions
   618  	var err error
   619  
   620  	switch cmd.Cmd {
   621  	case etf.Atom("events"):
   622  		events := cmd.Opt1.(etf.List)
   623  		numEvents := len(events)
   624  
   625  		subInternal, ok := p.producers[subscription.ID]
   626  		if !ok {
   627  			lib.Warning("consumer got %d events for unknown subscription %#v", numEvents, subscription)
   628  			return etf.Atom("ok"), nil
   629  		}
   630  		subInternal.count -= uint(numEvents)
   631  		if subInternal.count < 0 {
   632  			return nil, fmt.Errorf("got %d events which haven't bin requested", numEvents)
   633  		}
   634  		if numEvents < int(subInternal.options.MinDemand) {
   635  			return nil, fmt.Errorf("got %d events which is less than min %d", numEvents, subInternal.options.MinDemand)
   636  		}
   637  		if numEvents > int(subInternal.options.MaxDemand) {
   638  			return nil, fmt.Errorf("got %d events which is more than max %d", numEvents, subInternal.options.MaxDemand)
   639  		}
   640  
   641  		err = p.behavior.HandleEvents(p, subscription, events)
   642  		if err != nil {
   643  			return nil, err
   644  		}
   645  
   646  		// if subscription has auto demand we should request yet another
   647  		// bunch of events
   648  		if subInternal.count < subInternal.options.MinDemand && subInternal.options.ManualDemand == false {
   649  
   650  			cnt := subInternal.options.MaxDemand - subInternal.count
   651  			p.sendDemand(subInternal.Producer, subscription, cnt)
   652  			subInternal.count += cnt
   653  		}
   654  		return etf.Atom("ok"), nil
   655  
   656  	case etf.Atom("subscribed"):
   657  		if err := etf.TermProplistIntoStruct(cmd.Opt2, &subscriptionOpts); err != nil {
   658  			return nil, err
   659  		}
   660  
   661  		manualDemand, status := p.behavior.HandleSubscribed(p, subscription, subscriptionOpts)
   662  
   663  		if status != StageStatusOK {
   664  			return nil, status
   665  		}
   666  		subscriptionOpts.ManualDemand = manualDemand
   667  
   668  		producer := cmd.Opt1
   669  		subInternal := &subscriptionInternal{
   670  			Subscription: subscription,
   671  			Producer:     producer,
   672  			options:      subscriptionOpts,
   673  		}
   674  		p.producers[subscription.ID] = subInternal
   675  
   676  		if manualDemand == false {
   677  			p.sendDemand(producer, subscription, subInternal.options.MaxDemand)
   678  			subInternal.count = subInternal.options.MaxDemand
   679  		}
   680  
   681  		return etf.Atom("ok"), nil
   682  
   683  	case etf.Atom("retry-cancel"):
   684  		// if "subscribed" message hasn't still arrived then just ignore it
   685  		if _, ok := p.producers[subscription.ID]; !ok {
   686  			return etf.Atom("ok"), nil
   687  		}
   688  		fallthrough
   689  	case etf.Atom("cancel"):
   690  		// the subscription was canceled
   691  		reason, ok := cmd.Opt1.(string)
   692  		if !ok {
   693  			return nil, fmt.Errorf("Cancel reason is not a string")
   694  		}
   695  
   696  		subInternal, ok := p.producers[subscription.ID]
   697  		if !ok {
   698  			// There might be a case when "cancel" message arrives before
   699  			// the "subscribed" message due to async nature of messaging,
   700  			// so we should wait a bit and try to handle it one more time
   701  			// using "retry-cancel" message.
   702  			// I got this problem with GOMAXPROCS=1
   703  			msg := etf.Tuple{
   704  				etf.Atom("$gen_consumer"),
   705  				etf.Tuple{subscription.Pid, subscription.ID},
   706  				etf.Tuple{etf.Atom("retry-cancel"), reason},
   707  			}
   708  			// handle it in a second
   709  			p.SendAfter(p.Self(), msg, 200*time.Millisecond)
   710  			return etf.Atom("ok"), nil
   711  		}
   712  
   713  		// if we already handle MessageDown skip it
   714  		if reason != "noconnection" {
   715  			p.DemonitorProcess(subscription.ID)
   716  		}
   717  		delete(p.producers, subscription.ID)
   718  
   719  		err = p.behavior.HandleCanceled(p, subscription, reason)
   720  		if err != nil {
   721  			return nil, err
   722  		}
   723  
   724  		switch subInternal.options.Cancel {
   725  		case StageCancelTemporary:
   726  			return etf.Atom("ok"), nil
   727  		case StageCancelTransient:
   728  			if reason == "normal" || reason == "shutdown" {
   729  				return etf.Atom("ok"), nil
   730  			}
   731  			return nil, fmt.Errorf(reason)
   732  		default:
   733  			// StageCancelPermanent
   734  			return nil, fmt.Errorf(reason)
   735  		}
   736  	}
   737  
   738  	return nil, fmt.Errorf("unknown Stage command (HandleCast)")
   739  }
   740  
   741  func (p *StageProcess) handleProducer(subscription StageSubscription, cmd stageRequestCommand) (etf.Term, error) {
   742  	var subscriptionOpts StageSubscribeOptions
   743  	var err error
   744  
   745  	switch cmd.Cmd {
   746  	case etf.Atom("subscribe"):
   747  		// {subscribe, Cancel, Opts}
   748  		if err = etf.TermProplistIntoStruct(cmd.Opt2, &subscriptionOpts); err != nil {
   749  			return nil, err
   750  		}
   751  
   752  		// TODO handle cmd.Opts1 - could be etf.Atom("nil") or list of subscriptions
   753  		// for the cancelation
   754  
   755  		if subscriptionOpts.MinDemand > subscriptionOpts.MaxDemand {
   756  			msg := etf.Tuple{
   757  				etf.Atom("$gen_consumer"),
   758  				etf.Tuple{subscription.Pid, subscription.ID},
   759  				etf.Tuple{etf.Atom("cancel"), fmt.Errorf("MinDemand greater MaxDemand")},
   760  			}
   761  			p.Send(subscription.Pid, msg)
   762  			return etf.Atom("ok"), nil
   763  		}
   764  
   765  		err = p.behavior.HandleSubscribe(p, subscription, subscriptionOpts)
   766  
   767  		switch err {
   768  		case nil:
   769  			// cancel current subscription if this consumer has been already subscribed
   770  			if s, ok := p.consumers[subscription.Pid]; ok {
   771  				msg := etf.Tuple{
   772  					etf.Atom("$gen_consumer"),
   773  					etf.Tuple{subscription.Pid, s.Subscription.ID},
   774  					etf.Tuple{etf.Atom("cancel"), "resubscribed"},
   775  				}
   776  				p.Send(subscription.Pid, msg)
   777  				// notify dispatcher about cancelation the previous subscription
   778  				canceledSubscription := StageSubscription{
   779  					Pid: subscription.Pid,
   780  					ID:  s.Subscription.ID,
   781  				}
   782  				// cancel current demands
   783  				p.options.Dispatcher.Cancel(p.dispatcherState, canceledSubscription)
   784  				// notify dispatcher about the new subscription
   785  				if err := p.options.Dispatcher.Subscribe(p.dispatcherState, subscription, subscriptionOpts); err != nil {
   786  					// dispatcher can't handle this subscription
   787  					msg := etf.Tuple{
   788  						etf.Atom("$gen_consumer"),
   789  						etf.Tuple{subscription.Pid, s.Subscription.ID},
   790  						etf.Tuple{etf.Atom("cancel"), err.Error()},
   791  					}
   792  					p.Send(subscription.Pid, msg)
   793  					return etf.Atom("ok"), nil
   794  				}
   795  
   796  				s.Subscription = subscription
   797  				return etf.Atom("ok"), nil
   798  			}
   799  
   800  			if err := p.options.Dispatcher.Subscribe(p.dispatcherState, subscription, subscriptionOpts); err != nil {
   801  				// dispatcher can't handle this subscription
   802  				msg := etf.Tuple{
   803  					etf.Atom("$gen_consumer"),
   804  					etf.Tuple{subscription.Pid, subscription.ID},
   805  					etf.Tuple{etf.Atom("cancel"), err.Error()},
   806  				}
   807  				p.Send(subscription.Pid, msg)
   808  				return etf.Atom("ok"), nil
   809  			}
   810  
   811  			// monitor subscriber in order to remove this subscription
   812  			// if it terminated unexpectedly
   813  			m := p.MonitorProcess(subscription.Pid)
   814  			s := &subscriptionInternal{
   815  				Subscription: subscription,
   816  				Monitor:      m,
   817  				options:      subscriptionOpts,
   818  			}
   819  			p.consumers[subscription.Pid] = s
   820  			return etf.Atom("ok"), nil
   821  
   822  		case StageStatusNotAProducer:
   823  			// if it wasnt overloaded - send 'cancel' to the consumer
   824  			msg := etf.Tuple{
   825  				etf.Atom("$gen_consumer"),
   826  				etf.Tuple{subscription.Pid, subscription.ID},
   827  				etf.Tuple{etf.Atom("cancel"), err.Error()},
   828  			}
   829  			p.Send(subscription.Pid, msg)
   830  			return etf.Atom("ok"), nil
   831  
   832  		default:
   833  			// any other error should terminate this stage
   834  			return nil, err
   835  		}
   836  	case etf.Atom("retry-ask"):
   837  		// if the "subscribe" message hasn't still arrived, send a cancelation message
   838  		// to the consumer
   839  		if _, ok := p.consumers[subscription.Pid]; !ok {
   840  			msg := etf.Tuple{
   841  				etf.Atom("$gen_consumer"),
   842  				etf.Tuple{subscription.Pid, subscription.ID},
   843  				etf.Tuple{etf.Atom("cancel"), "not subscribed"},
   844  			}
   845  			p.Send(subscription.Pid, msg)
   846  			return etf.Atom("ok"), nil
   847  		}
   848  		fallthrough
   849  
   850  	case etf.Atom("ask"):
   851  		var events etf.List
   852  		var deliver []StageDispatchItem
   853  		var count uint
   854  		switch c := cmd.Opt1.(type) {
   855  		case int:
   856  			count = uint(c)
   857  		case uint:
   858  			count = c
   859  		default:
   860  			return nil, fmt.Errorf("Demand has wrong value %#v. Expected positive integer", cmd.Opt1)
   861  		}
   862  
   863  		// handle buffered demand on exit this function
   864  		defer func() {
   865  			if p.options.DisableDemandHandle {
   866  				return
   867  			}
   868  			if len(p.demandBuffer) == 0 {
   869  				return
   870  			}
   871  			d := p.demandBuffer[0]
   872  			msg := etf.Tuple{
   873  				etf.Atom("$gen_producer"),
   874  				etf.Tuple{d.subscription.Pid, d.subscription.ID},
   875  				etf.Tuple{etf.Atom("ask"), d.count},
   876  			}
   877  			p.Send(p.Self(), msg)
   878  			p.demandBuffer = p.demandBuffer[1:]
   879  		}()
   880  
   881  		if count == 0 {
   882  			// just ignore it
   883  			return etf.Atom("ok"), nil
   884  		}
   885  
   886  		if _, ok := p.consumers[subscription.Pid]; !ok {
   887  			// there might be a case when "ask" message arrives before
   888  			// the "subscribe" message due to async nature of messaging,
   889  			// so we should wait a bit and try to handle it one more time
   890  			// using "retry-ask" message
   891  			msg := etf.Tuple{
   892  				etf.Atom("$gen_producer"),
   893  				etf.Tuple{subscription.Pid, subscription.ID},
   894  				etf.Tuple{etf.Atom("retry-ask"), count},
   895  			}
   896  			// handle it in a second
   897  			p.SendAfter(p.Self(), msg, 1*time.Second)
   898  			return etf.Atom("ok"), nil
   899  		}
   900  
   901  		if p.options.DisableDemandHandle {
   902  			d := demandRequest{
   903  				subscription: subscription,
   904  				count:        count,
   905  			}
   906  			// FIXME it would be more effective to use sync.Pool with
   907  			// preallocated array behind the slice.
   908  			// see how it was made in lib.TakeBuffer
   909  			p.demandBuffer = append(p.demandBuffer, d)
   910  			return etf.Atom("ok"), nil
   911  		}
   912  
   913  		events, _ = p.behavior.HandleDemand(p, subscription, count)
   914  
   915  		// register this demand and trying to dispatch having events
   916  		dispatcher := p.options.Dispatcher
   917  		dispatcher.Ask(p.dispatcherState, subscription, count)
   918  		deliver = dispatcher.Dispatch(p.dispatcherState, events)
   919  		if len(deliver) == 0 {
   920  			return etf.Atom("ok"), nil
   921  		}
   922  
   923  		for d := range deliver {
   924  			msg := etf.Tuple{
   925  				etf.Atom("$gen_consumer"),
   926  				etf.Tuple{deliver[d].subscription.Pid, deliver[d].subscription.ID},
   927  				deliver[d].events,
   928  			}
   929  			p.Send(deliver[d].subscription.Pid, msg)
   930  		}
   931  
   932  		return etf.Atom("ok"), nil
   933  
   934  	case etf.Atom("cancel"):
   935  		var e error
   936  		// handle this cancelation in the dispatcher
   937  		dispatcher := p.options.Dispatcher
   938  		dispatcher.Cancel(p.dispatcherState, subscription)
   939  		reason := cmd.Opt1.(string)
   940  		// handle it in a Stage callback
   941  		e = p.behavior.HandleCancel(p, subscription, reason)
   942  		delete(p.consumers, subscription.Pid)
   943  		return etf.Atom("ok"), e
   944  	}
   945  
   946  	return nil, fmt.Errorf("unknown Stage command (HandleCall)")
   947  }
   948  
   949  func (p *StageProcess) handleStageDown(down MessageDown) error {
   950  	// remove subscription for producer and consumer. corner case - two
   951  	// processes have subscribed to each other.
   952  
   953  	// checking for subscribers (if we act as a producer).
   954  	// we monitor them by Pid only
   955  	if subInternal, ok := p.consumers[down.Pid]; ok {
   956  		// producer monitors consumer by the Pid and stores monitor reference
   957  		// in the subInternal struct
   958  		p.DemonitorProcess(subInternal.Monitor)
   959  		cmd := stageRequestCommand{
   960  			Cmd:  etf.Atom("cancel"),
   961  			Opt1: down.Reason,
   962  		}
   963  		if _, err := p.handleProducer(subInternal.Subscription, cmd); err != nil {
   964  			return err
   965  		}
   966  	}
   967  
   968  	// checking for producers (if we act as a consumer)
   969  	if subInternal, ok := p.producers[down.Ref]; ok {
   970  
   971  		cmd := stageRequestCommand{
   972  			Cmd:  etf.Atom("cancel"),
   973  			Opt1: down.Reason,
   974  		}
   975  
   976  		if _, err := p.handleConsumer(subInternal.Subscription, cmd); err != nil {
   977  			return err
   978  		}
   979  	}
   980  
   981  	return nil
   982  }
   983  
   984  // for the consumer side only
   985  func (p *StageProcess) sendDemand(producer etf.Term, subscription StageSubscription, count uint) {
   986  	msg := etf.Tuple{
   987  		etf.Atom("$gen_producer"),
   988  		etf.Tuple{p.Self(), subscription.ID},
   989  		etf.Tuple{etf.Atom("ask"), count},
   990  	}
   991  	p.Send(producer, msg)
   992  }