Merge lp:~tjoneslo/akiban-server/add-user-monitor into lp:~akiban-technologies/akiban-server/trunk

Proposed by Thomas Jones-Low
Status: Merged
Approved by: Nathan Williams
Approved revision: 2644
Merged at revision: 2646
Proposed branch: lp:~tjoneslo/akiban-server/add-user-monitor
Merge into: lp:~akiban-technologies/akiban-server/trunk
Diff against target: 432 lines (+233/-3)
10 files modified
src/main/java/com/akiban/server/service/is/ServerSchemaTablesServiceImpl.java (+60/-0)
src/main/java/com/akiban/server/service/monitor/MonitorService.java (+18/-0)
src/main/java/com/akiban/server/service/monitor/MonitorServiceImpl.java (+41/-1)
src/main/java/com/akiban/server/service/monitor/SessionMonitor.java (+3/-0)
src/main/java/com/akiban/server/service/monitor/SessionMonitorBase.java (+12/-0)
src/main/java/com/akiban/server/service/monitor/UserMonitor.java (+32/-0)
src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java (+13/-1)
src/main/java/com/akiban/server/service/security/UserMonitorImpl.java (+51/-0)
src/main/java/com/akiban/sql/pg/PostgresServerConnection.java (+1/-0)
src/test/java/com/akiban/server/service/is/SchemaTableServiceIT.java (+2/-1)
To merge this branch: bzr merge lp:~tjoneslo/akiban-server/add-user-monitor
Reviewer Review Type Date Requested Status
Nathan Williams Approve
Thomas Jones-Low Needs Resubmitting
Review via email: mp+161017@code.launchpad.net

Description of the change

Add a UserMonitor with IS.SERVER_USERS to show how much work each user is doing.

This creates a UserMonitor which is registered with the MontitorService and attached to the SessionMonitor when the user logs in. Currently this only works on the Postgres and InternalJDBC connections. If not using password or SSL security (i.e, security is none) there are no UserMonitors created.

This is mostly infrastructure. The only thing the UserMonitor counts currently is number of SQL statements executed. This can easily be extended, and will be at a later time.

This includes a new I_S.SERVER_USERS table to display the data gathered by the UserMonitor to the user. This table is sensitive to the restricted access security that the SERVER_SESSIONS (and other) tables are.

To post a comment you must log in.
Revision history for this message
Nathan Williams (nwilliams) wrote :

Tiny nit. Diff line 170 can just remove, instead of contains and remove. The string based contract being looser (i.e. not asserting) is fine though.

I'm not sure we'll want to blow away the monitor, diff line 322, if a user is deleted in general. Long term random guess: they still need billed. But I'm hand waving and we can always adjust as we figure out how we want to use it.

review: Needs Fixing
Revision history for this message
Thomas Jones-Low (tjoneslo) wrote :

There's a whole chunk of how we capture the session monitor (including the user monitor) data when the user logs out that needs to be captured before the logout is completed. Or the server is restarted. These are known issues that need to be addressed.

I've made the deregister cleaner per your request.

review: Needs Resubmitting
Revision history for this message
Nathan Williams (nwilliams) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/main/java/com/akiban/server/service/is/ServerSchemaTablesServiceImpl.java'
2--- src/main/java/com/akiban/server/service/is/ServerSchemaTablesServiceImpl.java 2013-04-23 22:45:16 +0000
3+++ src/main/java/com/akiban/server/service/is/ServerSchemaTablesServiceImpl.java 2013-04-26 00:32:25 +0000
4@@ -44,6 +44,7 @@
5 import com.akiban.server.service.monitor.PreparedStatementMonitor;
6 import com.akiban.server.service.monitor.ServerMonitor;
7 import com.akiban.server.service.monitor.SessionMonitor;
8+import com.akiban.server.service.monitor.UserMonitor;
9 import com.akiban.server.service.security.SecurityService;
10 import com.akiban.server.service.session.Session;
11 import com.akiban.server.store.SchemaManager;
12@@ -67,6 +68,7 @@
13 static final TableName SERVER_TAPS = new TableName (SCHEMA_NAME, "server_taps");
14 static final TableName SERVER_PREPARED_STATEMENTS = new TableName (SCHEMA_NAME, "server_prepared_statements");
15 static final TableName SERVER_CURSORS = new TableName (SCHEMA_NAME, "server_cursors");
16+ static final TableName SERVER_USERS = new TableName (SCHEMA_NAME, "server_users");
17
18 private final MonitorService monitor;
19 private final ConfigurationService configService;
20@@ -109,6 +111,8 @@
21 attach (ais, true, SERVER_PREPARED_STATEMENTS, PreparedStatements.class);
22 //SERVER_CURSORS
23 attach (ais, true, SERVER_CURSORS, Cursors.class);
24+ //SERVER_USERS
25+ attach(ais, true, SERVER_USERS, Users.class);
26 }
27
28 @Override
29@@ -136,6 +140,18 @@
30 }
31 }
32
33+ protected Collection<UserMonitor> getAccessibleUsers (Session session) {
34+ if (securityService.hasRestrictedAccess(session)) {
35+ return monitor.getUserMonitors();
36+ } else {
37+ UserMonitor um = monitor.getUserMonitor(session);
38+ if (um == null) {
39+ return Collections.emptyList();
40+ } else {
41+ return Collections.singletonList(um);
42+ }
43+ }
44+ }
45 private class InstanceSummary extends BasicFactoryBase {
46
47 public InstanceSummary(TableName sourceTable) {
48@@ -565,6 +581,46 @@
49 }
50 }
51
52+ private class Users extends BasicFactoryBase {
53+
54+ public Users(TableName sourceTable) {
55+ super(sourceTable);
56+ }
57+
58+ @Override
59+ public GroupScan getGroupScan(MemoryAdapter adapter) {
60+ return new Scan (adapter.getSession(), getRowType(adapter));
61+ }
62+
63+ @Override
64+ public long rowCount() {
65+ return monitor.getUserMonitors().size();
66+ }
67+
68+ private class Scan extends BaseScan {
69+ final Iterator<UserMonitor> users;
70+
71+ public Scan(Session session, RowType rowType) {
72+ super(rowType);
73+ users = getAccessibleUsers(session).iterator();
74+ }
75+
76+ @Override
77+ public Row next() {
78+ if (!users.hasNext()) {
79+ return null;
80+ }
81+ UserMonitor user = users.next();
82+ ValuesRow row = new ValuesRow (rowType,
83+ user.getUserName(),
84+ user.getStatementCount(),
85+ ++rowCounter);
86+ return row;
87+ }
88+ }
89+ }
90+
91+
92 static AkibanInformationSchema createTablesToRegister() {
93 NewAISBuilder builder = AISBBasedBuilder.create();
94
95@@ -635,6 +691,10 @@
96 .colTimestamp("creation_time", true)
97 .colBigInt("row_count", true);
98
99+ builder.userTable(SERVER_USERS)
100+ .colString("user_name", IDENT_MAX, false)
101+ .colBigInt("statement_count", false);
102+
103 return builder.ais(false);
104 }
105 }
106
107=== modified file 'src/main/java/com/akiban/server/service/monitor/MonitorService.java'
108--- src/main/java/com/akiban/server/service/monitor/MonitorService.java 2013-04-23 22:45:16 +0000
109+++ src/main/java/com/akiban/server/service/monitor/MonitorService.java 2013-04-26 00:32:25 +0000
110@@ -58,4 +58,22 @@
111
112 /** Log last statement from given monitor. */
113 void logQuery(SessionMonitor sessionMonitor);
114+
115+ /** Register the given User monitor. */
116+ void registerUserMonitor (UserMonitor userMonitor);
117+
118+ /** Deregister the given user montitor. */
119+ void deregisterUserMonitor (UserMonitor userMonitor);
120+
121+ /** Deregister the montor for the given user */
122+ void deregisterUserMonitor (String userName);
123+
124+ /** Get the user monitor for the given user name. */
125+ UserMonitor getUserMonitor(String userName);
126+
127+ /** Get the user monitor for the session user */
128+ UserMonitor getUserMonitor(Session session);
129+
130+ /** Get all the user monitors. */
131+ Collection<UserMonitor> getUserMonitors();
132 }
133
134=== modified file 'src/main/java/com/akiban/server/service/monitor/MonitorServiceImpl.java'
135--- src/main/java/com/akiban/server/service/monitor/MonitorServiceImpl.java 2013-04-23 22:45:16 +0000
136+++ src/main/java/com/akiban/server/service/monitor/MonitorServiceImpl.java 2013-04-26 00:32:25 +0000
137@@ -62,6 +62,8 @@
138 private BufferedWriter queryOut;
139
140 private long execTimeThreshold;
141+
142+ private Map<String, UserMonitor> users;
143
144 @Inject
145 public MonitorServiceImpl(ConfigurationService config) {
146@@ -76,6 +78,7 @@
147
148 sessionAllocator = new AtomicInteger();
149 sessions = new ConcurrentHashMap<>();
150+ users = new ConcurrentHashMap<>();
151
152 String enableLog = config.getProperty(QUERY_LOG_PROPERTY);
153 this.queryLogEnabled = new AtomicBoolean(Boolean.parseBoolean(enableLog));
154@@ -310,5 +313,42 @@
155 logger.debug("Query log file ready for writing.");
156 return true;
157 }
158-
159+
160+ /** Register the given User monitor. */
161+ @Override
162+ public void registerUserMonitor (UserMonitor userMonitor) {
163+ UserMonitor monitor = users.put(userMonitor.getUserName(), userMonitor);
164+ assert (monitor == null || monitor == userMonitor);
165+ }
166+
167+ /** Deregister the monitor for the given user */
168+ @Override
169+ public void deregisterUserMonitor (String userName) {
170+ users.remove(userName);
171+ }
172+
173+ /** Deregister the given user monitor. */
174+ @Override
175+ public void deregisterUserMonitor (UserMonitor userMonitor) {
176+ UserMonitor monitor = users.remove(userMonitor.getUserName());
177+ assert (monitor== null || monitor == userMonitor);
178+ }
179+
180+ /** Get the user monitor for the given user name. */
181+ @Override
182+ public UserMonitor getUserMonitor(String userName) {
183+ return users.get(userName);
184+ }
185+
186+ /** Get the user monitor for the session user */
187+ @Override
188+ public UserMonitor getUserMonitor(Session session) {
189+ return session.get(SESSION_KEY).getUserMonitor();
190+ }
191+
192+ /** Get all the user monitors. */
193+ @Override
194+ public Collection<UserMonitor> getUserMonitors() {
195+ return users.values();
196+ }
197 }
198
199=== modified file 'src/main/java/com/akiban/server/service/monitor/SessionMonitor.java'
200--- src/main/java/com/akiban/server/service/monitor/SessionMonitor.java 2013-03-22 20:05:57 +0000
201+++ src/main/java/com/akiban/server/service/monitor/SessionMonitor.java 2013-04-26 00:32:25 +0000
202@@ -76,4 +76,7 @@
203
204 /** Get any prepared statements. */
205 List<PreparedStatementMonitor> getPreparedStatements();
206+
207+ /** Get the user monitor for this session */
208+ UserMonitor getUserMonitor();
209 }
210
211=== modified file 'src/main/java/com/akiban/server/service/monitor/SessionMonitorBase.java'
212--- src/main/java/com/akiban/server/service/monitor/SessionMonitorBase.java 2013-03-22 20:05:57 +0000
213+++ src/main/java/com/akiban/server/service/monitor/SessionMonitorBase.java 2013-04-26 00:32:25 +0000
214@@ -32,6 +32,7 @@
215 private long currentStatementEndTime = -1;
216 private int rowsProcessed = 0;
217 private int statementCount = 0;
218+ private UserMonitor user = null;
219
220 protected SessionMonitorBase(int sessionID) {
221 this.sessionID = sessionID;
222@@ -65,6 +66,9 @@
223 public void endStatement(int rowsProcessed) {
224 currentStatementEndTime = System.currentTimeMillis();
225 this.rowsProcessed = rowsProcessed;
226+ if (user != null) {
227+ user.statementRun();
228+ }
229 }
230
231 // Caller can sequence all stages and avoid any gaps at the cost of more complicated
232@@ -167,5 +171,13 @@
233 public List<PreparedStatementMonitor> getPreparedStatements() {
234 return Collections.emptyList();
235 }
236+
237+ public void setUserMonitor(UserMonitor monitor) {
238+ this.user = monitor;
239+ }
240+
241+ public UserMonitor getUserMonitor() {
242+ return this.user;
243+ }
244
245 }
246
247=== added file 'src/main/java/com/akiban/server/service/monitor/UserMonitor.java'
248--- src/main/java/com/akiban/server/service/monitor/UserMonitor.java 1970-01-01 00:00:00 +0000
249+++ src/main/java/com/akiban/server/service/monitor/UserMonitor.java 2013-04-26 00:32:25 +0000
250@@ -0,0 +1,32 @@
251+/**
252+ * Copyright (C) 2009-2013 Akiban Technologies, Inc.
253+ *
254+ * This program is free software: you can redistribute it and/or modify
255+ * it under the terms of the GNU Affero General Public License as published by
256+ * the Free Software Foundation, either version 3 of the License, or
257+ * (at your option) any later version.
258+ *
259+ * This program is distributed in the hope that it will be useful,
260+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
261+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
262+ * GNU Affero General Public License for more details.
263+ *
264+ * You should have received a copy of the GNU Affero General Public License
265+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
266+ */
267+package com.akiban.server.service.monitor;
268+
269+public interface UserMonitor {
270+
271+ /** User name of the user being monitored */
272+ String getUserName();
273+
274+ /** The number of queries executed. */
275+ long getStatementCount();
276+
277+ /** Get total time in nanoseconds not spent idle. */
278+ long getNonIdleTimeNanos();
279+
280+ /** Flag a new statment has been processed */
281+ void statementRun();
282+}
283
284=== modified file 'src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java'
285--- src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java 2013-04-23 22:45:16 +0000
286+++ src/main/java/com/akiban/server/service/security/SecurityServiceImpl.java 2013-04-26 00:32:25 +0000
287@@ -27,6 +27,8 @@
288 import com.akiban.server.error.SecurityException;
289 import com.akiban.server.service.Service;
290 import com.akiban.server.service.config.ConfigurationService;
291+import com.akiban.server.service.monitor.MonitorService;
292+import com.akiban.server.service.monitor.UserMonitor;
293 import com.akiban.server.service.session.Session;
294 import com.akiban.server.store.SchemaManager;
295 import com.akiban.sql.server.ServerCallContextStack;
296@@ -81,6 +83,7 @@
297
298 private final ConfigurationService configService;
299 private final SchemaManager schemaManager;
300+ private final MonitorService monitor;
301
302 private boolean restrictUserSchema;
303
304@@ -88,9 +91,11 @@
305
306 @Inject
307 public SecurityServiceImpl(ConfigurationService configService,
308- SchemaManager schemaManager) {
309+ SchemaManager schemaManager,
310+ MonitorService monitor) {
311 this.configService = configService;
312 this.schemaManager = schemaManager;
313+ this.monitor = monitor;
314 }
315
316 // Connections are not thread safe, and prepared statements remember a Session,
317@@ -273,6 +278,7 @@
318 throw new SecurityException("Failed to delete user");
319 }
320 conn.commit();
321+ monitor.deregisterUserMonitor(name);
322 }
323 catch (SQLException ex) {
324 throw new SecurityException("Error deleting user", ex);
325@@ -336,6 +342,9 @@
326 if (session != null) {
327 session.put(SESSION_KEY, user);
328 }
329+ if (monitor.getUserMonitor(user.getName()) == null) {
330+ monitor.registerUserMonitor(new UserMonitorImpl(user.getName()));
331+ }
332 return user;
333 }
334
335@@ -367,6 +376,9 @@
336 if (session != null) {
337 session.put(SESSION_KEY, user);
338 }
339+ if (monitor.getUserMonitor(user.getName()) == null) {
340+ monitor.registerUserMonitor(new UserMonitorImpl(user.getName()));
341+ }
342 return user;
343 }
344
345
346=== added file 'src/main/java/com/akiban/server/service/security/UserMonitorImpl.java'
347--- src/main/java/com/akiban/server/service/security/UserMonitorImpl.java 1970-01-01 00:00:00 +0000
348+++ src/main/java/com/akiban/server/service/security/UserMonitorImpl.java 2013-04-26 00:32:25 +0000
349@@ -0,0 +1,51 @@
350+/**
351+ * Copyright (C) 2009-2013 Akiban Technologies, Inc.
352+ *
353+ * This program is free software: you can redistribute it and/or modify
354+ * it under the terms of the GNU Affero General Public License as published by
355+ * the Free Software Foundation, either version 3 of the License, or
356+ * (at your option) any later version.
357+ *
358+ * This program is distributed in the hope that it will be useful,
359+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
360+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
361+ * GNU Affero General Public License for more details.
362+ *
363+ * You should have received a copy of the GNU Affero General Public License
364+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
365+ */
366+package com.akiban.server.service.security;
367+
368+import java.util.concurrent.atomic.AtomicLong;
369+
370+import com.akiban.server.service.monitor.UserMonitor;
371+
372+public class UserMonitorImpl implements UserMonitor {
373+ private final String userName;
374+ protected final AtomicLong count = new AtomicLong();
375+
376+ public UserMonitorImpl (String name) {
377+ this.userName = name;
378+ }
379+ @Override
380+ public String getUserName() {
381+ return userName;
382+ }
383+
384+ @Override
385+ public long getStatementCount() {
386+ return count.get();
387+ }
388+
389+ @Override
390+ public void statementRun() {
391+ count.incrementAndGet();
392+ }
393+
394+ @Override
395+ public long getNonIdleTimeNanos() {
396+ // TODO Auto-generated method stub
397+ return 0;
398+ }
399+
400+}
401
402=== modified file 'src/main/java/com/akiban/sql/pg/PostgresServerConnection.java'
403--- src/main/java/com/akiban/sql/pg/PostgresServerConnection.java 2013-04-23 22:45:16 +0000
404+++ src/main/java/com/akiban/sql/pg/PostgresServerConnection.java 2013-04-26 00:32:25 +0000
405@@ -522,6 +522,7 @@
406 }
407 logger.debug("Login {}", (principal != null) ? principal : user);
408 authenticationOkay(user);
409+ sessionMonitor.setUserMonitor(reqs.monitor().getUserMonitor(user));
410 }
411
412 protected void authenticationOkay(String user) throws IOException {
413
414=== modified file 'src/test/java/com/akiban/server/service/is/SchemaTableServiceIT.java'
415--- src/test/java/com/akiban/server/service/is/SchemaTableServiceIT.java 2013-03-22 20:05:57 +0000
416+++ src/test/java/com/akiban/server/service/is/SchemaTableServiceIT.java 2013-04-26 00:32:25 +0000
417@@ -98,7 +98,7 @@
418
419 @Test
420 public void serverExamine() {
421- assertEquals ("Table count", 10, ServerSchemaTablesServiceImpl.createTablesToRegister().getUserTables().size());
422+ assertEquals ("Table count", 11, ServerSchemaTablesServiceImpl.createTablesToRegister().getUserTables().size());
423 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.ERROR_CODES));
424 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_INSTANCE_SUMMARY));
425 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_SERVERS));
426@@ -109,5 +109,6 @@
427 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_TAPS));
428 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_PREPARED_STATEMENTS));
429 assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_CURSORS));
430+ assertNotNull (ais.getUserTable(ServerSchemaTablesServiceImpl.SERVER_USERS));
431 }
432 }

Subscribers

People subscribed via source and target branches