1 /* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include <mysql/components/services/group_member_status_listener.h>
24 #include <mysql/components/services/group_membership_listener.h>
25 #include <mysql/components/services/log_builtins.h>
26
27 #include "plugin/group_replication/include/plugin.h"
28 #include "plugin/group_replication/include/services/notification/notification.h"
29
30 enum SvcTypes { kGroupMembership = 0, kGroupMemberStatus };
31
32 typedef int (*svc_notify_func)(Notification_context &, my_h_service);
33
notify_group_membership(Notification_context & ctx,my_h_service svc)34 static int notify_group_membership(Notification_context &ctx,
35 my_h_service svc) {
36 int svc_ko = 0;
37 const char *view_id = ctx.get_view_id().c_str();
38 SERVICE_TYPE(group_membership_listener) *listener = nullptr;
39
40 /* now that we have the handler for it, notify */
41 listener = reinterpret_cast<SERVICE_TYPE(group_membership_listener) *>(svc);
42
43 if (ctx.get_view_changed()) {
44 svc_ko = svc_ko + listener->notify_view_change(view_id);
45 }
46
47 if (ctx.get_quorum_lost()) {
48 svc_ko = svc_ko + listener->notify_quorum_loss(view_id);
49 }
50
51 return svc_ko;
52 }
53
notify_group_member_status(Notification_context & ctx,my_h_service svc)54 static int notify_group_member_status(Notification_context &ctx,
55 my_h_service svc) {
56 int svc_ko = 0;
57 const char *view_id = ctx.get_view_id().c_str();
58 SERVICE_TYPE(group_member_status_listener) *listener = nullptr;
59
60 /* now that we have the handler for it, notify */
61 listener =
62 reinterpret_cast<SERVICE_TYPE(group_member_status_listener) *>(svc);
63
64 if (ctx.get_member_state_changed()) {
65 svc_ko = svc_ko + listener->notify_member_state_change(view_id);
66 }
67
68 if (ctx.get_member_role_changed()) {
69 svc_ko = svc_ko + listener->notify_member_role_change(view_id);
70 }
71
72 return svc_ko;
73 }
74
75 /**
76 Auxiliary function to engage the service registry to
77 notify a set of listeners.
78
79 @param svc_type The service name.
80 @param ctx The events context
81
82 @return false on success, true otherwise.
83 */
notify(SvcTypes svc_type,Notification_context & ctx)84 static bool notify(SvcTypes svc_type, Notification_context &ctx) {
85 SERVICE_TYPE(registry) *r = nullptr;
86 SERVICE_TYPE(registry_query) *rq = nullptr;
87 my_h_service_iterator h_ret_it = nullptr;
88 my_h_service h_listener_svc = nullptr;
89 my_h_service h_listener_default_svc = nullptr;
90 bool res = false;
91 bool default_notified = false;
92 std::string svc_name;
93 svc_notify_func notify_func_ptr;
94
95 if (!registry_module || !(r = registry_module->get_registry_handle()) ||
96 !(rq = registry_module->get_registry_query_handle()))
97 goto err; /* purecov: inspected */
98
99 /*
100 Decides which listener service to notify, based on the
101 service type. It also checks whether the service should
102 be notified indeed, based on the event context.
103
104 If the event is not to be notified, the function returns
105 immediately.
106 */
107 switch (svc_type) {
108 case kGroupMembership:
109 notify_func_ptr = notify_group_membership;
110 svc_name = Registry_module_interface::SVC_NAME_MEMBERSHIP;
111 break;
112 case kGroupMemberStatus:
113 notify_func_ptr = notify_group_member_status;
114 svc_name = Registry_module_interface::SVC_NAME_STATUS;
115 break;
116 default:
117 DBUG_ASSERT(false); /* purecov: inspected */
118 /* production builds default to membership */
119 svc_name = Registry_module_interface::SVC_NAME_MEMBERSHIP;
120 notify_func_ptr = notify_group_membership;
121 break;
122 }
123
124 /* acquire the default service */
125 if (r->acquire(svc_name.c_str(), &h_listener_default_svc) ||
126 !h_listener_default_svc)
127 /* no listener registered, skip */
128 goto end;
129
130 /*
131 create iterator to navigate notification GMS change
132 notification listeners
133 */
134 if (rq->create(svc_name.c_str(), &h_ret_it)) {
135 goto err; /* purecov: inspected */
136 }
137
138 /* notify all listeners */
139 while (h_ret_it != nullptr &&
140 /* is_valid returns false on success */
141 rq->is_valid(h_ret_it) == false) {
142 int svc_ko = 0;
143 const char *next_svc_name = nullptr;
144
145 /* get next registered listener */
146 if (rq->get(h_ret_it, &next_svc_name)) goto err; /* purecov: inspected */
147
148 /*
149 The iterator currently contains more service implementations than
150 those named after the given service name. The spec says that the
151 name given is used to position the iterator start on the first
152 registered service implementation prefixed with that name. We need
153 to iterate until the next element in the iterator (service implementation)
154 has a different service name.
155 */
156 std::string s(next_svc_name);
157 if (s.find(svc_name, 0) == std::string::npos) break;
158
159 /* acquire next listener */
160 if (r->acquire(next_svc_name, &h_listener_svc))
161 goto err; /* purecov: inspected */
162
163 /* don't notify the default service twice */
164 if (h_listener_svc != h_listener_default_svc || !default_notified) {
165 if (notify_func_ptr(ctx, h_listener_svc))
166 LogPluginErr(WARNING_LEVEL,
167 ER_GRP_RPL_FAILED_TO_NOTIFY_GRP_MEMBERSHIP_EVENT,
168 next_svc_name); /* purecov: inspected */
169
170 default_notified =
171 default_notified || (h_listener_svc == h_listener_default_svc);
172 }
173
174 /* release the listener service */
175 if (r->release(h_listener_svc) || svc_ko) goto err; /* purecov: inspected */
176
177 /* update iterator */
178 if (rq->next(h_ret_it)) goto err; /* purecov: inspected */
179 }
180
181 end:
182 /* release the iterator */
183 if (h_ret_it) rq->release(h_ret_it);
184
185 /* release the default service */
186 if (h_listener_default_svc)
187 if (r->release(h_listener_default_svc)) res = true; /* purecov: inspected */
188
189 return res;
190
191 err:
192 res = true; /* purecov: inspected */
193 goto end;
194 }
195
196 /* Public Functions */
197
notify_and_reset_ctx(Notification_context & ctx)198 bool notify_and_reset_ctx(Notification_context &ctx) {
199 bool res = false;
200
201 if (ctx.get_view_changed() || ctx.get_quorum_lost()) {
202 /* notify membership events listeners. */
203 if (notify(kGroupMembership, ctx)) {
204 /* purecov: begin inspected */
205 LogPluginErr(ERROR_LEVEL,
206 ER_GRP_RPL_FAILED_TO_BROADCAST_GRP_MEMBERSHIP_NOTIFICATION);
207 /* purecov: end */
208 res = true; /* purecov: inspected */
209 }
210 }
211
212 if (ctx.get_member_state_changed() || ctx.get_member_role_changed()) {
213 /* notify member status events listeners. */
214 if (notify(kGroupMemberStatus, ctx)) {
215 /* purecov: begin inspected */
216 LogPluginErr(ERROR_LEVEL,
217 ER_GRP_RPL_FAILED_TO_BROADCAST_MEMBER_STATUS_NOTIFICATION);
218 /* purecov: end */
219 res = true; /* purecov: inspected */
220 }
221 }
222
223 ctx.reset();
224 return res;
225 }
226