File Coverage

blib/lib/MongoDB/_Server.pm
Criterion Covered Total %
statement 79 92 85.8
branch 23 30 76.6
condition 14 16 87.5
subroutine 26 28 92.8
pod 0 4 0.0
total 142 170 83.5


line stmt bran cond sub pod time code
1             # Copyright 2014 - present MongoDB, Inc.
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14              
15 60     60   72304 use strict;
  60         173  
  60         2105  
16 60     60   328 use warnings;
  60         143  
  60         2306  
17             package MongoDB::_Server;
18              
19 60     60   785 use version;
  60         2101  
  60         512  
20             our $VERSION = 'v2.2.0';
21              
22 60     60   6089 use Moo;
  60         10895  
  60         443  
23 60         424 use MongoDB::_Types qw(
24             Boolish
25             NonNegNum
26             HostAddress
27             ServerType
28             HostAddressList
29 60     60   24088 );
  60         179  
30 60         388 use Types::Standard qw(
31             InstanceOf
32             HashRef
33             Str
34             Num
35             Maybe
36 60     60   83514 );
  60         164  
37 60     60   68313 use List::Util qw/first/;
  60         161  
  60         4857  
38 60     60   940 use Time::HiRes qw/time/;
  60         1602  
  60         598  
39 60     60   8714 use namespace::clean -except => 'meta';
  60         9882  
  60         597  
40              
41             # address: the hostname or IP, and the port number, that the client connects
42             # to. Note that this is not the server's ismaster.me field, in the case that
43             # the server reports an address different from the address the client uses.
44              
45             has address => (
46             is => 'ro',
47             isa => HostAddress,
48             required => 1,
49             );
50              
51             # lastUpdateTime: when this server was last checked. Default "infinity ago".
52              
53             has last_update_time => (
54             is => 'ro',
55             isa => Num, # floating point time
56             required => 1,
57             );
58              
59             # error: information about the last error related to this server. Default null.
60              
61             has error => (
62             is => 'ro',
63             isa => Str,
64             default => '',
65             );
66              
67             # roundTripTime: the duration of the ismaster call. Default null.
68              
69             has rtt_sec => (
70             is => 'ro',
71             isa => NonNegNum,
72             default => 0,
73             );
74              
75             # is_master: hashref returned from an is_master command
76              
77             has is_master => (
78             is => 'ro',
79             isa => HashRef,
80             default => sub { {} },
81             );
82              
83             # compressor: hashref with id/callback values for used compression
84              
85             has compressor => (
86             is => 'ro',
87             isa => Maybe[HashRef],
88             );
89              
90             # type: a ServerType enum value. Default Unknown. Definitions from the Server
91             # Discovery and Monitoring Spec:
92             # - Unknown Initial, or after a network error or failed ismaster call, or "ok: 1"
93             # not in ismaster response.
94             # - Standalone No "msg: isdbgrid", no setName, and no "isreplicaset: true".
95             # - Mongos "msg: isdbgrid".
96             # - RSPrimary "ismaster: true", "setName" in response.
97             # - RSSecondary "secondary: true", "setName" in response.
98             # - RSArbiter "arbiterOnly: true", "setName" in response.
99             # - RSOther "setName" in response, "hidden: true" or not primary, secondary, nor arbiter.
100             # - RSGhost "isreplicaset: true" in response.
101             # - PossiblePrimary Not yet checked, but another member thinks it is the primary.
102              
103             has type => (
104             is => 'lazy',
105             isa => ServerType,
106             builder => '_build_type',
107             writer => '_set_type',
108             );
109              
110             sub _build_type {
111 540     540   5172 my ($self) = @_;
112 540         1283 my $is_master = $self->is_master;
113 540 100 66     2086 if ( !$is_master->{ok} ) {
    100          
    100          
    100          
114 322         4815 return 'Unknown';
115             }
116             elsif ( $is_master->{msg} && $is_master->{msg} eq 'isdbgrid' ) {
117 23         326 return 'Mongos';
118             }
119             elsif ( $is_master->{isreplicaset} ) {
120 3         60 return 'RSGhost';
121             }
122             elsif ( exists $is_master->{setName} ) {
123             return
124             $is_master->{ismaster} ? return 'RSPrimary'
125             : $is_master->{hidden} ? return 'RSOther'
126             : $is_master->{secondary} ? return 'RSSecondary'
127 171 100       1836 : $is_master->{arbiterOnly} ? return 'RSArbiter'
    100          
    100          
    100          
128             : 'RSOther';
129             }
130             else {
131 21         299 return 'Standalone';
132             }
133             }
134              
135             # hosts, passives, arbiters: Sets of addresses. This server's opinion of the
136             # replica set's members, if any. Default empty. The client monitors all three
137             # types of servers in a replica set.
138              
139             for my $s (qw/hosts passives arbiters/) {
140             has $s => (
141             is => 'lazy',
142             isa => HostAddressList,
143             builder => "_build_$s",
144             );
145              
146 60     60   42468 no strict 'refs'; ## no critic
  60         184  
  60         47641  
147             *{"_build_$s"} = sub {
148 867 100   867   40606 [ map { lc $_ } ( @{ $_[0]->is_master->{$s} || [] } ) ];
  196         1690  
  867         11244  
149             };
150             }
151              
152              
153             # address configured as part of replica set: string or null. Default null.
154              
155             has me => (
156             is => 'lazy',
157             isa => Str,
158             builder => "_build_me",
159             );
160              
161             sub _build_me {
162 289     289   12348 my ($self) = @_;
163 289   100     4215 return $self->is_master->{me} || '';
164             }
165              
166             # setName: string or null. Default null.
167              
168             has set_name => (
169             is => 'lazy',
170             isa => Str,
171             builder => "_build_set_name",
172             );
173              
174             sub _build_set_name {
175 188     188   184437 my ($self) = @_;
176 188   100     2912 return $self->is_master->{setName} || '';
177             }
178              
179             # primary: an address. This server's opinion of who the primary is. Default
180             # null.
181              
182             has primary => (
183             is => 'lazy',
184             isa => Str, # not HostAddress -- might be empty string
185             builder => "_build_primary",
186             );
187              
188             sub _build_primary {
189 289     289   15756 my ($self) = @_;
190 289   100     3980 return $self->is_master->{primary} || '';
191             }
192              
193             # tags: (a tag set) map from string to string. Default empty.
194              
195             has tags => (
196             is => 'lazy',
197             isa => HashRef,
198             builder => "_build_tags",
199             );
200              
201             sub _build_tags {
202 16     16   158 my ($self) = @_;
203 16   50     265 return $self->is_master->{tags} || {};
204             }
205              
206             # last_write_date: for replica set and wire version 5+ (converted to
207             # seconds)
208             has last_write_date => (
209             is => 'lazy',
210             isa => Num,
211             builder => "_build_last_write_date",
212             );
213              
214             sub _build_last_write_date {
215 54     54   483 my ($self) = @_;
216 54 50       175 return 0 unless exists $self->is_master->{lastWrite}{lastWriteDate};
217 54         196 return $self->is_master->{lastWrite}{lastWriteDate}->epoch;
218             }
219              
220             has is_available => (
221             is => 'lazy',
222             isa => Boolish,
223             builder => "_build_is_available",
224             );
225              
226             sub _build_is_available {
227 412     412   4007 my ($self) = @_;
228 412   100     5982 return $self->type ne 'Unknown' && $self->type ne 'PossiblePrimary';
229             }
230              
231             has is_readable => (
232             is => 'lazy',
233             isa => Boolish,
234             builder => "_build_is_readable",
235             );
236              
237             # any of these can take reads. Topologies will screen inappropriate
238             # ones out. E.g. "Standalone" won't be found in a replica set topology.
239             sub _build_is_readable {
240 0     0   0 my ($self) = @_;
241 0         0 my $type = $self->type;
242 0         0 return !! grep { $type eq $_ } qw/Standalone RSPrimary RSSecondary Mongos/;
  0         0  
243             }
244              
245             has is_writable => (
246             is => 'lazy',
247             isa => Boolish,
248             builder => "_build_is_writable",
249             );
250              
251             # any of these can take writes. Topologies will screen inappropriate
252             # ones out. E.g. "Standalone" won't be found in a replica set topology.
253             sub _build_is_writable {
254 24     24   230 my ($self) = @_;
255 24         370 my $type = $self->type;
256 24         179 return !! grep { $type eq $_ } qw/Standalone RSPrimary Mongos/;
  72         469  
257             }
258              
259             has is_data_bearing => (
260             is => 'lazy',
261             isa => Boolish,
262             builder => "_build_is_data_bearing",
263             );
264              
265             sub _build_is_data_bearing {
266 303     303   2955 my ( $self ) = @_;
267 303         4087 my $type = $self->type;
268 303         2134 return !! grep { $type eq $_ } qw/Standalone RSPrimary RSSecondary Mongos/;
  1212         5853  
269             }
270              
271             # logicalSessionTimeoutMinutes can be not set by a client
272             has logical_session_timeout_minutes => (
273             is => 'lazy',
274             isa => Maybe [NonNegNum],
275             builder => "_build_logical_session_timeout_minutes",
276             );
277              
278             sub _build_logical_session_timeout_minutes {
279 104     104   776 my ( $self ) = @_;
280 104   100     1610 return $self->is_master->{logicalSessionTimeoutMinutes} || undef;
281             }
282              
283             sub updated_since {
284 1166     1166 0 33654 my ( $self, $time ) = @_;
285 1166         15952 return( ($self->last_update_time - $time) > 0 );
286             }
287              
288             # check if server matches a single tag set (NOT a tag set list)
289             sub matches_tag_set {
290 58     58 0 112 my ( $self, $ts ) = @_;
291 60     60   551 no warnings 'uninitialized'; # let undef equal empty string without complaint
  60         156  
  60         21185  
292              
293 58         1038 my $tg = $self->tags;
294              
295             # check if ts is a subset of tg: if any tags in ts that aren't in tg or where
296             # the tag values aren't equal mean ts is NOT a subset
297 58 50   61   860 if ( !defined first { !exists( $tg->{$_} ) || $tg->{$_} ne $ts->{$_} } keys %$ts ) {
  61 100       293  
298 30         196 return 1;
299             }
300              
301 28         162 return;
302             }
303              
304             sub status_string {
305 53     53 0 982 my ($self) = @_;
306 53 50       586 if ( my $err = $self->error ) {
307 53         337 $err =~ tr[\n][ ];
308             return
309 53         1385 sprintf( "%s (type: %s, error: %s)", $self->{address}, $self->{type}, $err);
310             }
311             else {
312 0           return sprintf( "%s (type: %s)", map { $self->$_ } qw/address type/ );
  0            
313             }
314             }
315              
316             sub status_struct {
317 0     0 0   my ($self) = @_;
318 0           my $info = {
319             address => $self->address,
320             type => $self->type,
321             last_update_time => $self->last_update_time,
322             };
323 0 0         $info->{error} = $self->error if $self->error;
324 0 0         $info->{tags} = { %{ $self->tags } } if %{ $self->tags };
  0            
  0            
325 0           return $info;
326             }
327              
328              
329             1;
330              
331             # vim: ts=4 sts=4 sw=4 et: