summaryrefslogtreecommitdiff
path: root/server.go
blob: d873da637f727d480999a8d4b7462de01a659de2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package roadrunner

import (
	"sync"
	"os/exec"
	"fmt"
)

const (
	// EventNewPool triggered when server creates new pool.
	EventNewPool = 3

	// EventDestroyPool triggered when server destroys existed pool.
	EventDestroyPool = 4
)

// Server manages pool creation and swapping.
type Server struct {
	// observes pool events (can be attached to multiple pools at the same time)
	observer func(event int, ctx interface{})

	// worker command creator
	cmd func() *exec.Cmd

	// pool behaviour
	cfg Config

	// creates and connects to workers
	factory Factory

	// protects pool while the switch
	mu sync.Mutex

	// currently active pool instance
	pool Pool
}

// NewRouter creates new router. Make sure to call configure before the usage.
func NewRouter(cmd func() *exec.Cmd, factory Factory) *Server {
	return &Server{
		cmd:     cmd,
		factory: factory,
	}
}

// Configure configures underlying pool and destroys it's previous version if any.
func (r *Server) Configure(cfg Config) error {
	r.mu.Lock()
	previous := r.pool
	r.mu.Unlock()

	pool, err := NewPool(r.cmd, r.factory, cfg)
	if err != nil {
		return err
	}

	r.throw(EventNewPool, pool)

	r.mu.Lock()

	r.cfg, r.pool = cfg, pool
	r.pool.Observe(r.poolObserver)

	r.mu.Unlock()

	if previous != nil {
		go func(p Pool) {
			r.throw(EventDestroyPool, p)
			p.Destroy()
		}(previous)
	}

	return nil
}

// Reset resets the state of underlying pool and rebuilds all of it's workers.
func (r *Server) Reset() error {
	return r.Configure(r.cfg)
}

// Observe attaches event watcher to the router.
func (r *Server) Observe(o func(event int, ctx interface{})) {
	r.observer = o
}

// Pool returns active pool or error.
func (r *Server) Pool() (Pool) {
	r.mu.Lock()
	defer r.mu.Unlock()

	return r.pool
}

// Exec one task with given payload and context, returns result or error.
func (r *Server) Exec(rqs *Payload) (rsp *Payload, err error) {
	pool := r.Pool()
	if pool == nil {
		return nil, fmt.Errorf("no associared pool")
	}

	return pool.Exec(rqs)
}

// Workers returns worker list associated with the pool.
func (r *Server) Workers() (workers []*Worker) {
	pool := r.Pool()
	if pool == nil {
		return nil
	}

	return pool.Workers()
}

// Destroy all underlying pools and workers workers (but let them to complete the task).
func (r *Server) Destroy() {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.pool == nil {
		return
	}

	go func(p Pool) {
		r.throw(EventDestroyPool, p)
		p.Destroy()
	}(r.pool)

	r.pool = nil
}

// throw invokes event handler if any.
func (r *Server) throw(event int, ctx interface{}) {
	if r.observer != nil {
		r.observer(event, ctx)
	}
}

// Observe pool events.
func (r *Server) poolObserver(event int, ctx interface{}) {
	// bypassing to user specified observer
	r.throw(event, ctx)

	if event == EventPoolError {
		// pool failure, rebuilding
		r.Reset()
	}
}