Selector::picking()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 24
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 7
nop 1
dl 0
loc 24
rs 9.6111
c 0
b 0
f 0
1
<?php
2
/**
3
 * Route selector
4
 * User: moyo
5
 * Date: 27/10/2017
6
 * Time: 4:23 PM
7
 */
8
9
namespace Carno\Cluster\Routing;
10
11
use Carno\Cluster\Contracts\Tags;
12
use Carno\Cluster\Exception\UnavailableServiceNodesException;
13
use Carno\Cluster\Routing\Types\Forced;
14
use Carno\Net\Endpoint;
15
16
class Selector
17
{
18
    /**
19
     * @var string
20
     */
21
    private $serviced = null;
22
23
    /**
24
     * @var Endpoint[]
25
     */
26
    private $nodes = [];
27
28
    /**
29
     * @var Endpoint[][]
30
     */
31
    private $tagged = [];
32
33
    /**
34
     * @var Typeset
35
     */
36
    private $typeset = null;
37
38
    /**
39
     * Selector constructor.
40
     * @param string $serviced
41
     */
42
    public function __construct(string $serviced)
43
    {
44
        $this->serviced = $serviced;
45
        $this->typeset = new Typeset(new Forced);
46
    }
47
48
    /**
49
     * @return Typeset
50
     */
51
    public function typeset() : Typeset
52
    {
53
        return $this->typeset;
54
    }
55
56
    /**
57
     * @return bool
58
     */
59
    public function available() : bool
60
    {
61
        return count($this->nodes) > 0;
62
    }
63
64
    /**
65
     * @return bool
66
     */
67
    public function clustered() : bool
68
    {
69
        return count($this->nodes) > 1;
70
    }
71
72
    /**
73
     * @param string ...$tags
74
     * @return Endpoint
75
     */
76
    public function picking(string ...$tags) : Endpoint
77
    {
78
        // type forced
79
        if ($nodes = $this->typeset->picked()) {
80
            goto RND_PICK;
81
        }
82
83
        // tagged searching
84
        foreach ($tags as $tag) {
85
            if ($nodes = $this->tagged[$tag] ?? []) {
86
                goto RND_PICK;
87
            }
88
        }
89
90
        // fallback to master
91
        if ($nodes = $this->tagged[Tags::MASTER] ?? []) {
92
            goto RND_PICK;
93
        }
94
95
        // no services
96
        throw new UnavailableServiceNodesException($this->serviced);
97
98
        // random picking
99
        RND_PICK: return $nodes[array_rand($nodes)];
100
    }
101
102
    /**
103
     * @param Endpoint $node
104
     */
105
    public function classify(Endpoint $node) : void
106
    {
107
        foreach ($node->getTags() ?: Tags::DEFAULT as $tag) {
108
            if (substr($tag, 0, $cl = strlen(Tags::CMD)) === Tags::CMD) {
109
                $this->typeset->classify(substr($tag, $cl), $node);
110
            } else {
111
                $this->tagged[$tag][$node->id()] = $node;
112
            }
113
        }
114
115
        $this->nodes[$node->id()] = $node;
116
    }
117
118
    /**
119
     * @param Endpoint $node
120
     */
121
    public function release(Endpoint $node) : void
122
    {
123
        $this->typeset->release($node);
124
125
        foreach ($this->tagged as $tag => $nodes) {
126
            if (isset($nodes[$node->id()])) {
127
                unset($this->tagged[$tag][$node->id()]);
128
            }
129
        }
130
131
        unset($this->nodes[$node->id()]);
132
    }
133
}
134