passenger 4.0.0.rc6 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- data.tar.gz.asc +7 -7
- data/NEWS +14 -0
- data/build/basics.rb +2 -1
- data/build/packaging.rb +33 -0
- data/dev/run_travis.sh +1 -0
- data/doc/Architectural overview.html +1 -3
- data/doc/Packaging.txt.md +6 -6
- data/doc/Security of user switching support.html +1 -3
- data/doc/Users guide Apache.html +53 -20
- data/doc/Users guide Apache.idmap.txt +4 -0
- data/doc/Users guide Apache.txt +8 -0
- data/doc/Users guide Nginx.html +41 -18
- data/doc/Users guide Standalone.html +1 -3
- data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +11 -5
- data/doc/users_guide_snippets/installation.txt +5 -1
- data/doc/users_guide_snippets/tips.txt +10 -7
- data/doc/users_guide_snippets/under_the_hood/page_caching_support.txt +2 -2
- data/ext/apache2/Configuration.cpp +5 -6
- data/ext/apache2/Configuration.hpp +0 -4
- data/ext/common/ApplicationPool2/Group.h +25 -43
- data/ext/common/ApplicationPool2/Implementation.cpp +51 -32
- data/ext/common/ApplicationPool2/Pool.h +6 -7
- data/ext/common/ApplicationPool2/Process.h +61 -44
- data/ext/common/ApplicationPool2/Spawner.h +5 -0
- data/ext/common/BackgroundEventLoop.cpp +5 -11
- data/ext/common/Constants.h +1 -1
- data/ext/common/Utils.cpp +1 -1
- data/ext/common/agents/HelperAgent/AgentOptions.h +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler.h +2 -0
- data/ext/common/agents/LoggingAgent/LoggingServer.h +4 -1
- data/ext/common/agents/LoggingAgent/RemoteSender.h +58 -6
- data/lib/phusion_passenger.rb +2 -2
- data/lib/phusion_passenger/loader_shared_helpers.rb +6 -6
- data/lib/phusion_passenger/platform_info/compiler.rb +16 -0
- data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +1 -1
- data/lib/phusion_passenger/request_handler.rb +1 -0
- data/lib/phusion_passenger/standalone/start_command.rb +0 -4
- data/lib/phusion_passenger/utils/robust_interruption.rb +47 -28
- data/resources/templates/standalone/config.erb +4 -22
- data/test/config.json.example +1 -1
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +5 -3
- data/test/cxx/ApplicationPool2/PoolTest.cpp +75 -2
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +12 -7
- data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +18 -5
- data/test/cxx/RequestHandlerTest.cpp +3 -0
- data/test/ruby/shared/loader_sharedspec.rb +4 -0
- metadata +5 -6
- metadata.gz.asc +7 -7
- data/doc/Users guide Apache.index.sqlite3 +0 -0
@@ -428,7 +428,7 @@ public:
|
|
428
428
|
}
|
429
429
|
|
430
430
|
void inspectProcessList(const InspectOptions &options, stringstream &result,
|
431
|
-
const ProcessList &processes) const
|
431
|
+
const Group *group, const ProcessList &processes) const
|
432
432
|
{
|
433
433
|
ProcessList::const_iterator p_it;
|
434
434
|
for (p_it = processes.begin(); p_it != processes.end(); p_it++) {
|
@@ -453,8 +453,7 @@ public:
|
|
453
453
|
result << " Disabling..." << endl;
|
454
454
|
} else if (process->enabled == Process::DISABLED) {
|
455
455
|
result << " DISABLED" << endl;
|
456
|
-
}
|
457
|
-
if (process->getLifeStatus() == Process::SHUTTING_DOWN) {
|
456
|
+
} else if (process->enabled == Process::DETACHED) {
|
458
457
|
result << " Shutting down...";
|
459
458
|
}
|
460
459
|
|
@@ -1388,10 +1387,10 @@ public:
|
|
1388
1387
|
result << " (spawning new process...)" << endl;
|
1389
1388
|
}
|
1390
1389
|
result << " Requests in queue: " << group->getWaitlist.size() << endl;
|
1391
|
-
inspectProcessList(options, result, group->enabledProcesses);
|
1392
|
-
inspectProcessList(options, result, group->disablingProcesses);
|
1393
|
-
inspectProcessList(options, result, group->disabledProcesses);
|
1394
|
-
inspectProcessList(options, result, group->detachedProcesses);
|
1390
|
+
inspectProcessList(options, result, group, group->enabledProcesses);
|
1391
|
+
inspectProcessList(options, result, group, group->disablingProcesses);
|
1392
|
+
inspectProcessList(options, result, group, group->disabledProcesses);
|
1393
|
+
inspectProcessList(options, result, group, group->detachedProcesses);
|
1395
1394
|
result << endl;
|
1396
1395
|
}
|
1397
1396
|
}
|
@@ -128,7 +128,7 @@ class Process: public enable_shared_from_this<Process> {
|
|
128
128
|
private:
|
129
129
|
friend class Group;
|
130
130
|
|
131
|
-
/** A mutex to protect access to `
|
131
|
+
/** A mutex to protect access to `lifeStatus`. */
|
132
132
|
mutable boost::mutex lifetimeSyncher;
|
133
133
|
|
134
134
|
/** Group inside the Pool that this Process belongs to.
|
@@ -200,9 +200,10 @@ public:
|
|
200
200
|
* the admin socket need not be closed, etc.
|
201
201
|
*/
|
202
202
|
bool dummy;
|
203
|
-
/** Whether it is required that
|
204
|
-
* this Process. Normally true, except for dummy Process
|
205
|
-
* Pool::asyncGet() with options.noop == true
|
203
|
+
/** Whether it is required that triggerShutdown() and cleanup() must be called
|
204
|
+
* before destroying this Process. Normally true, except for dummy Process
|
205
|
+
* objects created by Pool::asyncGet() with options.noop == true, because those
|
206
|
+
* processes are never added to Group.enabledProcesses.
|
206
207
|
*/
|
207
208
|
bool requiresShutdown;
|
208
209
|
|
@@ -224,19 +225,21 @@ public:
|
|
224
225
|
int sessions;
|
225
226
|
/** Number of sessions opened so far. */
|
226
227
|
unsigned int processed;
|
227
|
-
/** Do not access directly, always use `isAlive()`/`
|
228
|
+
/** Do not access directly, always use `isAlive()`/`isDead()`/`getLifeStatus()` or
|
228
229
|
* through `lifetimeSyncher`. */
|
229
230
|
enum LifeStatus {
|
230
231
|
/** Up and operational. */
|
231
232
|
ALIVE,
|
232
|
-
/**
|
233
|
-
*
|
234
|
-
|
235
|
-
|
233
|
+
/** This process has been detached, and the detached processes checker has
|
234
|
+
* verified that there are no active sessions left and has told the process
|
235
|
+
* to shut down. In this state we're supposed to wait until the process
|
236
|
+
* has actually shutdown, after which clean() must be called. */
|
237
|
+
SHUTDOWN_TRIGGERED,
|
236
238
|
/**
|
237
|
-
*
|
239
|
+
* The process has exited and clean() has been called. In this state,
|
240
|
+
* this object is no longer usable.
|
238
241
|
*/
|
239
|
-
|
242
|
+
DEAD
|
240
243
|
} lifeStatus;
|
241
244
|
enum EnabledStatus {
|
242
245
|
/** Up and operational. */
|
@@ -250,7 +253,14 @@ public:
|
|
250
253
|
* requests. It *may* still handle some requests, e.g. by
|
251
254
|
* the Out-of-Band-Work trigger.
|
252
255
|
*/
|
253
|
-
DISABLED
|
256
|
+
DISABLED,
|
257
|
+
/**
|
258
|
+
* Process has been detached. It will be removed from the Group
|
259
|
+
* as soon we have detected that the OS process has exited. Detached
|
260
|
+
* processes are allowed to finish their requests, but are not
|
261
|
+
* eligible for new requests.
|
262
|
+
*/
|
263
|
+
DETACHED
|
254
264
|
} enabled;
|
255
265
|
enum OobwStatus {
|
256
266
|
/** Process is not using out-of-band work. */
|
@@ -330,15 +340,19 @@ public:
|
|
330
340
|
}
|
331
341
|
|
332
342
|
~Process() {
|
333
|
-
if (OXT_UNLIKELY(!
|
334
|
-
P_BUG("You must call Process::
|
343
|
+
if (OXT_UNLIKELY(!isDead() && requiresShutdown)) {
|
344
|
+
P_BUG("You must call Process::triggerShutdown() and Process::cleanup() before actually "
|
335
345
|
"destroying the Process object.");
|
336
346
|
}
|
337
347
|
}
|
338
348
|
|
339
|
-
static void
|
349
|
+
static void forceTriggerShutdownAndCleanup(ProcessPtr process) {
|
340
350
|
if (process != NULL) {
|
341
|
-
process->
|
351
|
+
process->triggerShutdown();
|
352
|
+
// Pretend like the OS process has exited so
|
353
|
+
// that the canCleanup() precondition is true.
|
354
|
+
process->m_osProcessExists = false;
|
355
|
+
process->cleanup();
|
342
356
|
}
|
343
357
|
}
|
344
358
|
|
@@ -348,7 +362,7 @@ public:
|
|
348
362
|
* @post result != NULL
|
349
363
|
*/
|
350
364
|
const GroupPtr getGroup() const {
|
351
|
-
assert(!
|
365
|
+
assert(!isDead());
|
352
366
|
return group.lock();
|
353
367
|
}
|
354
368
|
|
@@ -359,7 +373,7 @@ public:
|
|
359
373
|
|
360
374
|
/**
|
361
375
|
* Thread-safe.
|
362
|
-
* @pre getLifeState() !=
|
376
|
+
* @pre getLifeState() != DEAD
|
363
377
|
* @post result != NULL
|
364
378
|
*/
|
365
379
|
SuperGroupPtr getSuperGroup() const;
|
@@ -371,9 +385,15 @@ public:
|
|
371
385
|
}
|
372
386
|
|
373
387
|
// Thread-safe.
|
374
|
-
bool
|
388
|
+
bool hasTriggeredShutdown() const {
|
375
389
|
lock_guard<boost::mutex> lock(lifetimeSyncher);
|
376
|
-
return lifeStatus ==
|
390
|
+
return lifeStatus == SHUTDOWN_TRIGGERED;
|
391
|
+
}
|
392
|
+
|
393
|
+
// Thread-safe.
|
394
|
+
bool isDead() const {
|
395
|
+
lock_guard<boost::mutex> lock(lifetimeSyncher);
|
396
|
+
return lifeStatus == DEAD;
|
377
397
|
}
|
378
398
|
|
379
399
|
// Thread-safe.
|
@@ -382,32 +402,30 @@ public:
|
|
382
402
|
return lifeStatus;
|
383
403
|
}
|
384
404
|
|
385
|
-
|
405
|
+
bool canTriggerShutdown() const {
|
406
|
+
return getLifeStatus() == ALIVE && sessions == 0;
|
407
|
+
}
|
408
|
+
|
409
|
+
void triggerShutdown() {
|
410
|
+
assert(canTriggerShutdown());
|
386
411
|
{
|
387
412
|
lock_guard<boost::mutex> lock(lifetimeSyncher);
|
388
413
|
assert(lifeStatus == ALIVE);
|
389
|
-
lifeStatus =
|
414
|
+
lifeStatus = SHUTDOWN_TRIGGERED;
|
390
415
|
}
|
391
416
|
if (!dummy) {
|
392
417
|
syscalls::shutdown(adminSocket, SHUT_WR);
|
393
418
|
}
|
394
419
|
}
|
395
420
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
// Some code have guards that call process->shutdown().
|
400
|
-
// Returning instead of enforcing !isShutdown() makes things easier.
|
401
|
-
return;
|
402
|
-
}
|
403
|
-
|
404
|
-
assert(sessions == 0);
|
421
|
+
bool canCleanup() const {
|
422
|
+
return getLifeStatus() == SHUTDOWN_TRIGGERED && !osProcessExists();
|
423
|
+
}
|
405
424
|
|
406
|
-
|
407
|
-
|
408
|
-
}
|
425
|
+
void cleanup() {
|
426
|
+
assert(canCleanup());
|
409
427
|
|
410
|
-
P_TRACE(2, "
|
428
|
+
P_TRACE(2, "Cleaning up process " << inspect());
|
411
429
|
if (!dummy) {
|
412
430
|
if (OXT_LIKELY(sockets != NULL)) {
|
413
431
|
SocketList::const_iterator it, end = sockets->end();
|
@@ -421,11 +439,7 @@ public:
|
|
421
439
|
}
|
422
440
|
|
423
441
|
lock_guard<boost::mutex> lock(lifetimeSyncher);
|
424
|
-
lifeStatus =
|
425
|
-
}
|
426
|
-
|
427
|
-
bool canBeShutDown() const {
|
428
|
-
return sessions == 0 && !osProcessExists();
|
442
|
+
lifeStatus = DEAD;
|
429
443
|
}
|
430
444
|
|
431
445
|
/** Checks whether the OS process exists.
|
@@ -535,11 +549,11 @@ public:
|
|
535
549
|
case ALIVE:
|
536
550
|
stream << "<life_status>ALIVE</life_status>";
|
537
551
|
break;
|
538
|
-
case
|
539
|
-
stream << "<life_status>
|
552
|
+
case SHUTDOWN_TRIGGERED:
|
553
|
+
stream << "<life_status>SHUTDOWN_TRIGGERED</life_status>";
|
540
554
|
break;
|
541
|
-
case
|
542
|
-
stream << "<life_status>
|
555
|
+
case DEAD:
|
556
|
+
stream << "<life_status>DEAD</life_status>";
|
543
557
|
break;
|
544
558
|
default:
|
545
559
|
P_BUG("Unknown 'lifeStatus' state " << (int) lifeStatus);
|
@@ -554,6 +568,9 @@ public:
|
|
554
568
|
case DISABLED:
|
555
569
|
stream << "<enabled>DISABLED</enabled>";
|
556
570
|
break;
|
571
|
+
case DETACHED:
|
572
|
+
stream << "<enabled>DETACHED</enabled>";
|
573
|
+
break;
|
557
574
|
default:
|
558
575
|
P_BUG("Unknown 'enabled' state " << (int) enabled);
|
559
576
|
}
|
@@ -801,6 +801,7 @@ protected:
|
|
801
801
|
return;
|
802
802
|
}
|
803
803
|
|
804
|
+
UPDATE_TRACE_POINT();
|
804
805
|
string defaultGroup;
|
805
806
|
string startupFile = absolutizePath(options.getStartupFile(), info.appRoot);
|
806
807
|
struct passwd *userInfo = NULL;
|
@@ -823,6 +824,7 @@ protected:
|
|
823
824
|
defaultGroup = options.defaultGroup;
|
824
825
|
}
|
825
826
|
|
827
|
+
UPDATE_TRACE_POINT();
|
826
828
|
if (!options.user.empty()) {
|
827
829
|
userInfo = getpwnam(options.user.c_str());
|
828
830
|
} else {
|
@@ -838,6 +840,7 @@ protected:
|
|
838
840
|
userInfo = getpwnam(options.defaultUser.c_str());
|
839
841
|
}
|
840
842
|
|
843
|
+
UPDATE_TRACE_POINT();
|
841
844
|
if (!options.group.empty()) {
|
842
845
|
if (options.group == "!STARTUP_FILE!") {
|
843
846
|
struct stat buf;
|
@@ -857,6 +860,7 @@ protected:
|
|
857
860
|
groupInfo = getgrnam(defaultGroup.c_str());
|
858
861
|
}
|
859
862
|
|
863
|
+
UPDATE_TRACE_POINT();
|
860
864
|
if (userInfo == NULL) {
|
861
865
|
throw RuntimeException("Cannot determine a user to lower privilege to");
|
862
866
|
}
|
@@ -864,6 +868,7 @@ protected:
|
|
864
868
|
throw RuntimeException("Cannot determine a group to lower privilege to");
|
865
869
|
}
|
866
870
|
|
871
|
+
UPDATE_TRACE_POINT();
|
867
872
|
#ifdef __APPLE__
|
868
873
|
int groups[1024];
|
869
874
|
info.ngroups = sizeof(groups) / sizeof(int);
|
@@ -63,17 +63,7 @@ startBackgroundLoop(BackgroundEventLoop *bg) {
|
|
63
63
|
|
64
64
|
BackgroundEventLoop::BackgroundEventLoop(bool scalable) {
|
65
65
|
TRACE_POINT();
|
66
|
-
|
67
|
-
if (loop == NULL) {
|
68
|
-
loop = ev_loop_new(EVBACKEND_KQUEUE);
|
69
|
-
}
|
70
|
-
if (loop == NULL) {
|
71
|
-
loop = ev_loop_new(0);
|
72
|
-
}
|
73
|
-
if (loop == NULL) {
|
74
|
-
throw RuntimeException("Cannot create an event loop");
|
75
|
-
}
|
76
|
-
|
66
|
+
|
77
67
|
if (scalable) {
|
78
68
|
loop = ev_loop_new(EVBACKEND_KQUEUE);
|
79
69
|
if (loop == NULL) {
|
@@ -85,6 +75,10 @@ BackgroundEventLoop::BackgroundEventLoop(bool scalable) {
|
|
85
75
|
} else {
|
86
76
|
loop = ev_loop_new(EVBACKEND_POLL);
|
87
77
|
}
|
78
|
+
if (loop == NULL) {
|
79
|
+
throw RuntimeException("Cannot create an event loop");
|
80
|
+
}
|
81
|
+
|
88
82
|
async = (ev_async *) malloc(sizeof(ev_async));
|
89
83
|
async->data = this;
|
90
84
|
ev_async_init(async, signalBackgroundEventLoopExit);
|
data/ext/common/Constants.h
CHANGED
data/ext/common/Utils.cpp
CHANGED
@@ -391,7 +391,7 @@ getProcessUsername() {
|
|
391
391
|
result = (struct passwd *) NULL;
|
392
392
|
}
|
393
393
|
|
394
|
-
if (result == (struct passwd *) NULL) {
|
394
|
+
if (result == (struct passwd *) NULL || result->pw_name == NULL || result->pw_name[0] == '\0') {
|
395
395
|
snprintf(strings, sizeof(strings), "UID %lld", (long long) getuid());
|
396
396
|
strings[sizeof(strings) - 1] = '\0';
|
397
397
|
return strings;
|
@@ -1646,6 +1646,8 @@ private:
|
|
1646
1646
|
options.loggingAgentAddress = this->options.loggingAgentAddress;
|
1647
1647
|
options.loggingAgentUsername = "logging";
|
1648
1648
|
options.loggingAgentPassword = this->options.loggingAgentPassword;
|
1649
|
+
options.defaultUser = this->options.defaultUser;
|
1650
|
+
options.defaultGroup = this->options.defaultGroup;
|
1649
1651
|
fillPoolOption(client, options.appGroupName, "PASSENGER_APP_GROUP_NAME");
|
1650
1652
|
fillPoolOption(client, options.appType, "PASSENGER_APP_TYPE");
|
1651
1653
|
fillPoolOption(client, options.environment, "PASSENGER_ENV");
|
@@ -1129,7 +1129,10 @@ public:
|
|
1129
1129
|
TransactionMap::const_iterator end = transactions.end();
|
1130
1130
|
|
1131
1131
|
stream << "Number of clients : " << getClients().size() << "\n";
|
1132
|
-
stream << "
|
1132
|
+
stream << "\n";
|
1133
|
+
|
1134
|
+
stream << "RemoteSender:\n";
|
1135
|
+
remoteSender.inspect(stream);
|
1133
1136
|
stream << "\n";
|
1134
1137
|
|
1135
1138
|
LogSinkCache::const_iterator sit;
|
@@ -33,12 +33,14 @@
|
|
33
33
|
|
34
34
|
#include <boost/shared_ptr.hpp>
|
35
35
|
#include <boost/bind.hpp>
|
36
|
+
#include <boost/foreach.hpp>
|
36
37
|
#include <oxt/thread.hpp>
|
37
38
|
#include <string>
|
38
39
|
#include <list>
|
39
40
|
|
40
41
|
#include <Logging.h>
|
41
42
|
#include <StaticString.h>
|
43
|
+
#include <Utils.h>
|
42
44
|
#include <Utils/BlockingQueue.h>
|
43
45
|
#include <Utils/SystemTime.h>
|
44
46
|
#include <Utils/ScopeGuard.h>
|
@@ -164,6 +166,10 @@ private:
|
|
164
166
|
}
|
165
167
|
curl_slist_free_all(headers);
|
166
168
|
}
|
169
|
+
|
170
|
+
string name() const {
|
171
|
+
return ip + ":" + toString(port);
|
172
|
+
}
|
167
173
|
|
168
174
|
bool ping() {
|
169
175
|
P_DEBUG("Pinging Union Station gateway " << ip << ":" << port);
|
@@ -258,12 +264,13 @@ private:
|
|
258
264
|
BlockingQueue<Item> queue;
|
259
265
|
oxt::thread *thr;
|
260
266
|
|
267
|
+
mutable boost::mutex syncher;
|
261
268
|
list<ServerPtr> servers;
|
262
269
|
time_t nextCheckupTime;
|
270
|
+
unsigned int packetsSent, packetsDropped;
|
263
271
|
|
264
272
|
void threadMain() {
|
265
273
|
ScopeGuard guard(boost::bind(&RemoteSender::freeThreadData, this));
|
266
|
-
nextCheckupTime = 0;
|
267
274
|
|
268
275
|
while (true) {
|
269
276
|
Item item;
|
@@ -292,21 +299,22 @@ private:
|
|
292
299
|
}
|
293
300
|
|
294
301
|
bool firstStarted() const {
|
302
|
+
lock_guard<boost::mutex> l(syncher);
|
295
303
|
return nextCheckupTime == 0;
|
296
304
|
}
|
297
305
|
|
298
306
|
void recheckServers() {
|
299
|
-
|
307
|
+
P_INFO("Rechecking Union Station gateway servers (" << gatewayAddress << ")...");
|
300
308
|
|
301
309
|
vector<string> ips;
|
302
310
|
vector<string>::const_iterator it;
|
311
|
+
list<ServerPtr> servers;
|
303
312
|
string hostName;
|
304
313
|
bool someServersAreDown = false;
|
305
314
|
|
306
315
|
ips = resolveHostname(gatewayAddress, gatewayPort);
|
307
|
-
|
316
|
+
P_INFO(ips.size() << " Union Station gateway servers found");
|
308
317
|
|
309
|
-
servers.clear();
|
310
318
|
for (it = ips.begin(); it != ips.end(); it++) {
|
311
319
|
ServerPtr server = make_shared<Server>(*it, gatewayAddress, gatewayPort,
|
312
320
|
certificate, &proxyInfo);
|
@@ -316,7 +324,7 @@ private:
|
|
316
324
|
someServersAreDown = true;
|
317
325
|
}
|
318
326
|
}
|
319
|
-
|
327
|
+
P_INFO(servers.size() << " Union Station gateway servers are up");
|
320
328
|
|
321
329
|
if (servers.empty()) {
|
322
330
|
scheduleNextCheckup(5 * 60);
|
@@ -325,9 +333,13 @@ private:
|
|
325
333
|
} else {
|
326
334
|
scheduleNextCheckup(3 * 60 * 60);
|
327
335
|
}
|
336
|
+
|
337
|
+
lock_guard<boost::mutex> l(syncher);
|
338
|
+
this->servers = servers;
|
328
339
|
}
|
329
340
|
|
330
341
|
void freeThreadData() {
|
342
|
+
lock_guard<boost::mutex> l(syncher);
|
331
343
|
servers.clear(); // Invoke destructors inside this thread.
|
332
344
|
}
|
333
345
|
|
@@ -345,6 +357,7 @@ private:
|
|
345
357
|
}
|
346
358
|
|
347
359
|
unsigned int msecUntilNextCheckup() const {
|
360
|
+
lock_guard<boost::mutex> l(syncher);
|
348
361
|
time_t now = SystemTime::get();
|
349
362
|
if (now >= nextCheckupTime) {
|
350
363
|
return 0;
|
@@ -354,10 +367,12 @@ private:
|
|
354
367
|
}
|
355
368
|
|
356
369
|
bool timeForCheckup() const {
|
370
|
+
lock_guard<boost::mutex> l(syncher);
|
357
371
|
return SystemTime::get() >= nextCheckupTime;
|
358
372
|
}
|
359
373
|
|
360
374
|
void sendOut(const Item &item) {
|
375
|
+
unique_lock<boost::mutex> l(syncher);
|
361
376
|
bool sent = false;
|
362
377
|
bool someServersWentDown = false;
|
363
378
|
|
@@ -365,12 +380,18 @@ private:
|
|
365
380
|
// Pick first available server and put it on the back of the list
|
366
381
|
// for round-robin load balancing.
|
367
382
|
ServerPtr server = servers.front();
|
368
|
-
|
383
|
+
l.unlock();
|
369
384
|
if (server->send(item)) {
|
385
|
+
l.lock();
|
386
|
+
servers.pop_front();
|
370
387
|
servers.push_back(server);
|
371
388
|
sent = true;
|
389
|
+
packetsSent++;
|
372
390
|
} else {
|
391
|
+
l.lock();
|
392
|
+
servers.pop_front();
|
373
393
|
someServersWentDown = true;
|
394
|
+
packetsDropped++;
|
374
395
|
}
|
375
396
|
}
|
376
397
|
|
@@ -386,6 +407,13 @@ private:
|
|
386
407
|
* effectively dropped until after the next checkup has detected
|
387
408
|
* servers that are up.
|
388
409
|
*/
|
410
|
+
if (!sent) {
|
411
|
+
P_WARN("Dropping Union Station packet because no servers are available: "
|
412
|
+
"key=" << item.unionStationKey <<
|
413
|
+
", node=" << item.nodeName <<
|
414
|
+
", category=" << item.category <<
|
415
|
+
", compressedDataSize=" << item.data.size());
|
416
|
+
}
|
389
417
|
}
|
390
418
|
|
391
419
|
bool compress(const StaticString data[], unsigned int count, string &output) {
|
@@ -443,6 +471,9 @@ public:
|
|
443
471
|
throw RuntimeException("Invalid Union Station proxy address \"" +
|
444
472
|
proxyAddress + "\": " + e.what());
|
445
473
|
}
|
474
|
+
nextCheckupTime = 0;
|
475
|
+
packetsSent = 0;
|
476
|
+
packetsDropped = 0;
|
446
477
|
thr = new oxt::thread(
|
447
478
|
boost::bind(&RemoteSender::threadMain, this),
|
448
479
|
"RemoteSender thread",
|
@@ -494,12 +525,33 @@ public:
|
|
494
525
|
|
495
526
|
if (!queue.tryAdd(item)) {
|
496
527
|
P_WARN("The Union Station gateway isn't responding quickly enough; dropping packet.");
|
528
|
+
lock_guard<boost::mutex> l(syncher);
|
529
|
+
packetsDropped++;
|
497
530
|
}
|
498
531
|
}
|
499
532
|
|
500
533
|
unsigned int queued() const {
|
501
534
|
return queue.size();
|
502
535
|
}
|
536
|
+
|
537
|
+
template<typename Stream>
|
538
|
+
void inspect(Stream &stream) const {
|
539
|
+
lock_guard<boost::mutex> l(syncher);
|
540
|
+
stream << " Available servers (" << servers.size() << "): ";
|
541
|
+
foreach (const ServerPtr server, servers) {
|
542
|
+
stream << server->name() << " ";
|
543
|
+
}
|
544
|
+
stream << "\n";
|
545
|
+
stream << " Items in queue: " << queue.size() << "\n";
|
546
|
+
stream << " Packet sent out so far: " << packetsSent << "\n";
|
547
|
+
stream << " Packet dropped out so far: " << packetsDropped << "\n";
|
548
|
+
stream << " Next server checkup time: ";
|
549
|
+
if (nextCheckupTime == 0) {
|
550
|
+
stream << "not yet scheduled, waiting for first packet\n";
|
551
|
+
} else {
|
552
|
+
stream << "in " << distanceOfTimeInWords(nextCheckupTime) << "\n";
|
553
|
+
}
|
554
|
+
}
|
503
555
|
};
|
504
556
|
|
505
557
|
|