passenger 4.0.42 → 4.0.43
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.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/CHANGELOG +13 -0
- data/CONTRIBUTING.md +2 -19
- data/build/agents.rb +4 -1
- data/build/cxx_tests.rb +7 -2
- data/build/debian.rb +1 -1
- data/debian.template/control.template +0 -2
- data/doc/CodingTipsAndPitfalls.md +56 -0
- data/doc/Users guide Apache.idmap.txt +16 -14
- data/doc/Users guide Nginx.idmap.txt +8 -6
- data/doc/Users guide Standalone.idmap.txt +3 -1
- data/doc/Users guide Standalone.txt +1 -1
- data/doc/users_guide_snippets/environment_variables.txt +1 -0
- data/doc/users_guide_snippets/installation.txt +5 -5
- data/doc/users_guide_snippets/support_information.txt +42 -9
- data/doc/users_guide_snippets/troubleshooting/default.txt +42 -0
- data/ext/common/ApplicationPool2/Common.h +1 -0
- data/ext/common/ApplicationPool2/DirectSpawner.h +2 -7
- data/ext/common/ApplicationPool2/DummySpawner.h +1 -1
- data/ext/common/ApplicationPool2/Group.h +4 -2
- data/ext/common/ApplicationPool2/Options.h +9 -7
- data/ext/common/ApplicationPool2/Pool.h +83 -40
- data/ext/common/ApplicationPool2/Process.h +2 -6
- data/ext/common/ApplicationPool2/README.md +0 -40
- data/ext/common/ApplicationPool2/SmartSpawner.h +2 -9
- data/ext/common/ApplicationPool2/Spawner.h +1 -4
- data/ext/common/ApplicationPool2/SpawnerFactory.h +6 -9
- data/ext/common/ApplicationPool2/SuperGroup.h +3 -3
- data/ext/common/Constants.h +1 -1
- data/ext/common/UnionStation/Connection.h +227 -0
- data/ext/common/UnionStation/Core.h +497 -0
- data/ext/common/UnionStation/ScopeLog.h +172 -0
- data/ext/common/UnionStation/Transaction.h +276 -0
- data/ext/common/Utils.cpp +83 -8
- data/ext/common/Utils.h +25 -4
- data/ext/common/Utils/AnsiColorConstants.h +1 -0
- data/ext/common/Utils/ProcessMetricsCollector.h +6 -170
- data/ext/common/Utils/SpeedMeter.h +258 -0
- data/ext/common/Utils/StrIntUtils.cpp +6 -0
- data/ext/common/Utils/StringScanning.h +277 -0
- data/ext/common/Utils/SystemMetricsCollector.h +1460 -0
- data/ext/common/agents/Base.cpp +8 -8
- data/ext/common/agents/HelperAgent/Main.cpp +12 -6
- data/ext/common/agents/HelperAgent/RequestHandler.h +15 -16
- data/ext/common/agents/HelperAgent/SystemMetricsTool.cpp +199 -0
- data/ext/common/agents/LoggingAgent/LoggingServer.h +2 -1
- data/ext/common/agents/SpawnPreparer.cpp +20 -32
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/config/list_instances_command.rb +118 -0
- data/lib/phusion_passenger/config/main.rb +22 -4
- data/lib/phusion_passenger/config/system_metrics_command.rb +37 -0
- data/lib/phusion_passenger/config/utils.rb +1 -1
- data/lib/phusion_passenger/loader_shared_helpers.rb +8 -5
- data/lib/phusion_passenger/platform_info/compiler.rb +1 -1
- data/resources/templates/error_layout.html.template +3 -3
- data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +3 -5
- data/test/cxx/ApplicationPool2/PoolTest.cpp +1 -3
- data/test/cxx/ApplicationPool2/ProcessTest.cpp +4 -4
- data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +5 -7
- data/test/cxx/RequestHandlerTest.cpp +9 -3
- data/test/cxx/UnionStationTest.cpp +61 -64
- metadata +13 -4
- metadata.gz.asc +7 -7
- data/ext/common/UnionStation.h +0 -968
- data/helper-scripts/system-memory-stats.py +0 -207
@@ -118,3 +118,45 @@ cd /path-to-your-app
|
|
118
118
|
passenger start --temp-dir=$HOME/tmp
|
119
119
|
---------------------------------------
|
120
120
|
endif::[]
|
121
|
+
|
122
|
+
=== I get "command not found" when running a Phusion Passenger command through sudo
|
123
|
+
|
124
|
+
**Symptoms**::
|
125
|
+
Phusion Passenger commands can be found as a normal user, but not when run through sudo:
|
126
|
+
+
|
127
|
+
-------------------------
|
128
|
+
$ passenger-status
|
129
|
+
...some output, but no "command not found" error...
|
130
|
+
$ passenger-install-apache2-module
|
131
|
+
...some output, but no "command not found" error...
|
132
|
+
$ sudo passenger-status
|
133
|
+
sudo: passenger-status: command not found
|
134
|
+
$ sudo passenger-install-apache2-module
|
135
|
+
sudo: passenger-install-apache2-module: command not found
|
136
|
+
-------------------------
|
137
|
+
|
138
|
+
**Cause**::
|
139
|
+
The operating system looks up commands using <<the_path_env_var,the PATH environment variable>>. However, sudo resets all environment variables to a default value, dictated by sudo. If Phusion Passenger was installed to a location that is not in the default sudo PATH value, then sudo will not be able to find the Phusion Passenger commands.
|
140
|
+
+
|
141
|
+
In addition, if you installed Phusion Passenger using a Ruby interpreter that was installed through RVM, then you **must** use rvmsudo instead of sudo. As a rule, when you're an RVM user, always use rvmsudo instead of sudo.
|
142
|
+
|
143
|
+
**Solution**::
|
144
|
+
Execute the command using its full path. You can use `which` as a normal user to lookup the full path:
|
145
|
+
+
|
146
|
+
---------------------------------------
|
147
|
+
$ which passenger-status
|
148
|
+
/somewhere/bin/passenger-status
|
149
|
+
---------------------------------------
|
150
|
+
+
|
151
|
+
Next, run full path of the command using either sudo or rvmsudo:
|
152
|
+
+
|
153
|
+
---------------------------------------
|
154
|
+
$ sudo /somewhere/bin/passenger-status
|
155
|
+
|
156
|
+
# -OR, if you're using RVM:-
|
157
|
+
|
158
|
+
$ rvmsudo /somewhere/bin/passenger-status
|
159
|
+
---------------------------------------
|
160
|
+
|
161
|
+
**Recommended reading**::
|
162
|
+
When using sudo, you will probably run into similar "command not found" issues in the future, with components other than Phusion Passenger. We **strongly recommend** you to <<about_environment_variables,learn about environment variables>> so that you know what to do in the future.
|
@@ -40,8 +40,6 @@ using namespace oxt;
|
|
40
40
|
|
41
41
|
class DirectSpawner: public Spawner {
|
42
42
|
private:
|
43
|
-
SafeLibevPtr libev;
|
44
|
-
|
45
43
|
static int startBackgroundThread(void *(*mainFunction)(void *), void *arg) {
|
46
44
|
// Using raw pthread API because we don't want to register such
|
47
45
|
// trivial threads on the oxt::thread list.
|
@@ -146,12 +144,10 @@ private:
|
|
146
144
|
}
|
147
145
|
|
148
146
|
public:
|
149
|
-
DirectSpawner(const
|
150
|
-
const ResourceLocator &_resourceLocator,
|
147
|
+
DirectSpawner(const ResourceLocator &_resourceLocator,
|
151
148
|
const ServerInstanceDir::GenerationPtr &_generation,
|
152
149
|
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
153
|
-
: Spawner(_resourceLocator)
|
154
|
-
libev(_libev)
|
150
|
+
: Spawner(_resourceLocator)
|
155
151
|
{
|
156
152
|
generation = _generation;
|
157
153
|
if (_config == NULL) {
|
@@ -218,7 +214,6 @@ public:
|
|
218
214
|
|
219
215
|
NegotiationDetails details;
|
220
216
|
details.preparation = &preparation;
|
221
|
-
details.libev = libev;
|
222
217
|
details.stderrCapturer =
|
223
218
|
make_shared<BackgroundIOCapturer>(
|
224
219
|
errorPipe.first,
|
@@ -63,7 +63,7 @@ public:
|
|
63
63
|
|
64
64
|
boost::lock_guard<boost::mutex> l(lock);
|
65
65
|
count++;
|
66
|
-
ProcessPtr process = boost::make_shared<Process>(
|
66
|
+
ProcessPtr process = boost::make_shared<Process>(
|
67
67
|
(pid_t) count, "gupid-" + toString(count),
|
68
68
|
toString(count),
|
69
69
|
adminSocket.second, FileDescriptor(), sockets,
|
@@ -425,7 +425,9 @@ public:
|
|
425
425
|
&& (newOptions.maxRequestQueueSize == 0
|
426
426
|
|| getWaitlist.size() < newOptions.maxRequestQueueSize)))
|
427
427
|
{
|
428
|
-
getWaitlist.push_back(GetWaiter(
|
428
|
+
getWaitlist.push_back(GetWaiter(
|
429
|
+
newOptions.copyAndPersist().detachFromUnionStationTransaction(),
|
430
|
+
callback));
|
429
431
|
return true;
|
430
432
|
} else {
|
431
433
|
P_WARN("Request queue is full. Returning an error");
|
@@ -900,7 +902,7 @@ public:
|
|
900
902
|
}
|
901
903
|
|
902
904
|
if (OXT_UNLIKELY(newOptions.noop)) {
|
903
|
-
ProcessPtr process = boost::make_shared<Process>(
|
905
|
+
ProcessPtr process = boost::make_shared<Process>(
|
904
906
|
0, string(), string(),
|
905
907
|
FileDescriptor(), FileDescriptor(),
|
906
908
|
SocketListPtr(), 0, 0);
|
@@ -31,7 +31,8 @@
|
|
31
31
|
#include <boost/shared_array.hpp>
|
32
32
|
#include <ApplicationPool2/AppTypes.h>
|
33
33
|
#include <Account.h>
|
34
|
-
#include <UnionStation.h>
|
34
|
+
#include <UnionStation/Core.h>
|
35
|
+
#include <UnionStation/Transaction.h>
|
35
36
|
#include <Constants.h>
|
36
37
|
#include <ResourceLocator.h>
|
37
38
|
#include <StaticString.h>
|
@@ -361,10 +362,11 @@ public:
|
|
361
362
|
StaticString uri;
|
362
363
|
|
363
364
|
/**
|
364
|
-
*
|
365
|
-
* in which case Union Station logging is
|
365
|
+
* The Union Station log transaction that this request belongs to.
|
366
|
+
* May be the null pointer, in which case Union Station logging is
|
367
|
+
* disabled for this request.
|
366
368
|
*/
|
367
|
-
UnionStation::
|
369
|
+
UnionStation::TransactionPtr transaction;
|
368
370
|
|
369
371
|
/**
|
370
372
|
* A sticky session ID for routing to a specific process.
|
@@ -529,11 +531,11 @@ public:
|
|
529
531
|
uri = StaticString();
|
530
532
|
stickySessionId = 0;
|
531
533
|
noop = false;
|
532
|
-
return
|
534
|
+
return detachFromUnionStationTransaction();
|
533
535
|
}
|
534
536
|
|
535
|
-
Options &
|
536
|
-
|
537
|
+
Options &detachFromUnionStationTransaction() {
|
538
|
+
transaction.reset();
|
537
539
|
return *this;
|
538
540
|
}
|
539
541
|
|
@@ -47,7 +47,8 @@
|
|
47
47
|
#include <ApplicationPool2/Session.h>
|
48
48
|
#include <ApplicationPool2/SpawnerFactory.h>
|
49
49
|
#include <ApplicationPool2/Options.h>
|
50
|
-
#include <UnionStation.h>
|
50
|
+
#include <UnionStation/Core.h>
|
51
|
+
#include <UnionStation/Transaction.h>
|
51
52
|
#include <Logging.h>
|
52
53
|
#include <Exceptions.h>
|
53
54
|
#include <RandomGenerator.h>
|
@@ -58,6 +59,7 @@
|
|
58
59
|
#include <Utils/MessagePassing.h>
|
59
60
|
#include <Utils/VariantMap.h>
|
60
61
|
#include <Utils/ProcessMetricsCollector.h>
|
62
|
+
#include <Utils/SystemMetricsCollector.h>
|
61
63
|
|
62
64
|
namespace Passenger {
|
63
65
|
namespace ApplicationPool2 {
|
@@ -88,9 +90,6 @@ public:
|
|
88
90
|
public:
|
89
91
|
friend class SuperGroup;
|
90
92
|
friend class Group;
|
91
|
-
typedef UnionStation::LoggerFactory LoggerFactory;
|
92
|
-
typedef UnionStation::LoggerFactoryPtr LoggerFactoryPtr;
|
93
|
-
typedef UnionStation::LoggerPtr LoggerPtr;
|
94
93
|
|
95
94
|
struct DebugSupport {
|
96
95
|
/** Mailbox for the unit tests to receive messages on. */
|
@@ -126,8 +125,10 @@ public:
|
|
126
125
|
typedef boost::shared_ptr<DebugSupport> DebugSupportPtr;
|
127
126
|
|
128
127
|
SpawnerFactoryPtr spawnerFactory;
|
129
|
-
|
128
|
+
UnionStation::CorePtr unionStationCore;
|
130
129
|
RandomGeneratorPtr randomGenerator;
|
130
|
+
SystemMetricsCollector systemMetricsCollector;
|
131
|
+
SystemMetrics systemMetrics;
|
131
132
|
|
132
133
|
mutable boost::mutex syncher;
|
133
134
|
unsigned int max;
|
@@ -730,14 +731,13 @@ public:
|
|
730
731
|
return sleepTime;
|
731
732
|
}
|
732
733
|
|
733
|
-
struct
|
734
|
+
struct UnionStationLogEntry {
|
734
735
|
string groupName;
|
736
|
+
const char *category;
|
735
737
|
string key;
|
736
|
-
|
738
|
+
string data;
|
737
739
|
};
|
738
740
|
|
739
|
-
typedef boost::shared_ptr<ProcessAnalyticsLogEntry> ProcessAnalyticsLogEntryPtr;
|
740
|
-
|
741
741
|
static void collectAnalytics(PoolPtr self) {
|
742
742
|
TRACE_POINT();
|
743
743
|
syscalls::usleep(3000000);
|
@@ -780,6 +780,43 @@ public:
|
|
780
780
|
}
|
781
781
|
}
|
782
782
|
|
783
|
+
void prepareUnionStationProcessStateLogs(vector<UnionStationLogEntry> &logEntries,
|
784
|
+
const GroupPtr &group) const
|
785
|
+
{
|
786
|
+
if (group->options.analytics && unionStationCore != NULL) {
|
787
|
+
logEntries.push_back(UnionStationLogEntry());
|
788
|
+
UnionStationLogEntry &entry = logEntries.back();
|
789
|
+
stringstream stream;
|
790
|
+
|
791
|
+
stream << "Group: <group>";
|
792
|
+
group->inspectXml(stream, false);
|
793
|
+
stream << "</group>";
|
794
|
+
|
795
|
+
entry.groupName = group->name;
|
796
|
+
entry.category = "processes";
|
797
|
+
entry.key = group->options.unionStationKey;
|
798
|
+
entry.data = stream.str();
|
799
|
+
}
|
800
|
+
}
|
801
|
+
|
802
|
+
void prepareUnionStationSystemMetricsLogs(vector<UnionStationLogEntry> &logEntries,
|
803
|
+
const GroupPtr &group) const
|
804
|
+
{
|
805
|
+
if (group->options.analytics && unionStationCore != NULL) {
|
806
|
+
logEntries.push_back(UnionStationLogEntry());
|
807
|
+
UnionStationLogEntry &entry = logEntries.back();
|
808
|
+
stringstream stream;
|
809
|
+
|
810
|
+
stream << "System metrics: ";
|
811
|
+
systemMetrics.toXml(stream);
|
812
|
+
|
813
|
+
entry.groupName = group->name;
|
814
|
+
entry.category = "system_metrics";
|
815
|
+
entry.key = group->options.unionStationKey;
|
816
|
+
entry.data = stream.str();
|
817
|
+
}
|
818
|
+
}
|
819
|
+
|
783
820
|
unsigned long long realCollectAnalytics() {
|
784
821
|
TRACE_POINT();
|
785
822
|
this_thread::disable_interruption di;
|
@@ -813,20 +850,27 @@ public:
|
|
813
850
|
}
|
814
851
|
}
|
815
852
|
|
816
|
-
|
853
|
+
// Collect process metrics and system and store them in the
|
854
|
+
// data structures. Later, we log them to Union Station.
|
855
|
+
ProcessMetricMap processMetrics;
|
817
856
|
try {
|
818
|
-
// Now collect the process metrics and store them in the
|
819
|
-
// data structures, and log the state into the analytics logs.
|
820
857
|
UPDATE_TRACE_POINT();
|
821
|
-
|
822
|
-
} catch (const
|
858
|
+
processMetrics = ProcessMetricsCollector().collect(pids);
|
859
|
+
} catch (const ParseException &) {
|
823
860
|
P_WARN("Unable to collect process metrics: cannot parse 'ps' output.");
|
824
861
|
goto end;
|
825
862
|
}
|
863
|
+
try {
|
864
|
+
UPDATE_TRACE_POINT();
|
865
|
+
systemMetricsCollector.collect(systemMetrics);
|
866
|
+
} catch (const RuntimeException &e) {
|
867
|
+
P_WARN("Unable to collect system metrics: " << e.what());
|
868
|
+
goto end;
|
869
|
+
}
|
826
870
|
|
827
871
|
{
|
828
872
|
UPDATE_TRACE_POINT();
|
829
|
-
vector<
|
873
|
+
vector<UnionStationLogEntry> logEntries;
|
830
874
|
vector<ProcessPtr> processesToDetach;
|
831
875
|
vector<Callback> actions;
|
832
876
|
ScopedLock l(syncher);
|
@@ -840,22 +884,11 @@ public:
|
|
840
884
|
for (g_it = superGroup->groups.begin(); g_it != g_end; g_it++) {
|
841
885
|
const GroupPtr &group = *g_it;
|
842
886
|
|
843
|
-
updateProcessMetrics(group->enabledProcesses,
|
844
|
-
updateProcessMetrics(group->disablingProcesses,
|
845
|
-
updateProcessMetrics(group->disabledProcesses,
|
846
|
-
|
847
|
-
|
848
|
-
if (group->options.analytics && loggerFactory != NULL) {
|
849
|
-
ProcessAnalyticsLogEntryPtr entry = boost::make_shared<ProcessAnalyticsLogEntry>();
|
850
|
-
stringstream &xml = entry->data;
|
851
|
-
|
852
|
-
entry->groupName = group->name;
|
853
|
-
entry->key = group->options.unionStationKey;
|
854
|
-
xml << "Group: <group>";
|
855
|
-
group->inspectXml(xml, false);
|
856
|
-
xml << "</group>";
|
857
|
-
logEntries.push_back(entry);
|
858
|
-
}
|
887
|
+
updateProcessMetrics(group->enabledProcesses, processMetrics, processesToDetach);
|
888
|
+
updateProcessMetrics(group->disablingProcesses, processMetrics, processesToDetach);
|
889
|
+
updateProcessMetrics(group->disabledProcesses, processMetrics, processesToDetach);
|
890
|
+
prepareUnionStationProcessStateLogs(logEntries, group);
|
891
|
+
prepareUnionStationSystemMetricsLogs(logEntries, group);
|
859
892
|
}
|
860
893
|
}
|
861
894
|
|
@@ -869,11 +902,14 @@ public:
|
|
869
902
|
l.unlock();
|
870
903
|
UPDATE_TRACE_POINT();
|
871
904
|
while (!logEntries.empty()) {
|
872
|
-
|
905
|
+
UnionStationLogEntry &entry = logEntries.back();
|
906
|
+
UnionStation::TransactionPtr transaction =
|
907
|
+
unionStationCore->newTransaction(
|
908
|
+
entry.groupName,
|
909
|
+
entry.category,
|
910
|
+
entry.key);
|
911
|
+
transaction->message(entry.data);
|
873
912
|
logEntries.pop_back();
|
874
|
-
LoggerPtr logger = loggerFactory->newTransaction(entry->groupName,
|
875
|
-
"processes", entry->key);
|
876
|
-
logger->message(entry->data.str());
|
877
913
|
}
|
878
914
|
|
879
915
|
UPDATE_TRACE_POINT();
|
@@ -923,19 +959,25 @@ public:
|
|
923
959
|
|
924
960
|
public:
|
925
961
|
Pool(const SpawnerFactoryPtr &spawnerFactory,
|
926
|
-
const
|
962
|
+
const UnionStation::CorePtr &unionStationCore = UnionStation::CorePtr(),
|
927
963
|
const RandomGeneratorPtr &randomGenerator = RandomGeneratorPtr(),
|
928
964
|
const VariantMap *agentsOptions = NULL)
|
929
965
|
{
|
930
966
|
this->spawnerFactory = spawnerFactory;
|
931
|
-
this->
|
967
|
+
this->unionStationCore = unionStationCore;
|
932
968
|
if (randomGenerator != NULL) {
|
933
969
|
this->randomGenerator = randomGenerator;
|
934
970
|
} else {
|
935
971
|
this->randomGenerator = boost::make_shared<RandomGenerator>();
|
936
972
|
}
|
937
973
|
this->agentsOptions = agentsOptions;
|
938
|
-
|
974
|
+
|
975
|
+
try {
|
976
|
+
systemMetricsCollector.collect(systemMetrics);
|
977
|
+
} catch (const RuntimeException &e) {
|
978
|
+
P_WARN("Unable to collect system metrics: " << e.what());
|
979
|
+
}
|
980
|
+
|
939
981
|
lifeStatus = ALIVE;
|
940
982
|
max = 6;
|
941
983
|
maxIdleTime = 60 * 1000000;
|
@@ -1075,7 +1117,7 @@ public:
|
|
1075
1117
|
*/
|
1076
1118
|
P_DEBUG("Could not free a process; putting request to top-level getWaitlist");
|
1077
1119
|
getWaitlist.push_back(GetWaiter(
|
1078
|
-
options.copyAndPersist().
|
1120
|
+
options.copyAndPersist().detachFromUnionStationTransaction(),
|
1079
1121
|
callback));
|
1080
1122
|
} else {
|
1081
1123
|
/* Now that a process has been trashed we can create
|
@@ -1538,8 +1580,9 @@ public:
|
|
1538
1580
|
ProcessList::const_iterator p_it;
|
1539
1581
|
|
1540
1582
|
result << "<?xml version=\"1.0\" encoding=\"iso8859-1\" ?>\n";
|
1541
|
-
result << "<info version=\"
|
1583
|
+
result << "<info version=\"3\">";
|
1542
1584
|
|
1585
|
+
result << "<passenger_version>" << PASSENGER_VERSION << "</passenger_version>";
|
1543
1586
|
result << "<process_count>" << getProcessCount(false) << "</process_count>";
|
1544
1587
|
result << "<max>" << max << "</max>";
|
1545
1588
|
result << "<capacity_used>" << capacityUsed(false) << "</capacity_used>";
|
@@ -41,7 +41,6 @@
|
|
41
41
|
#include <ApplicationPool2/PipeWatcher.h>
|
42
42
|
#include <Constants.h>
|
43
43
|
#include <FileDescriptor.h>
|
44
|
-
#include <SafeLibev.h>
|
45
44
|
#include <Logging.h>
|
46
45
|
#include <Utils/PriorityQueue.h>
|
47
46
|
#include <Utils/SystemTime.h>
|
@@ -209,8 +208,6 @@ public:
|
|
209
208
|
* written to again. Reading is thread-safe.
|
210
209
|
*************************************************************/
|
211
210
|
|
212
|
-
/** The libev event loop to use. */
|
213
|
-
SafeLibev * const libev;
|
214
211
|
/** Process PID. */
|
215
212
|
pid_t pid;
|
216
213
|
/** An ID that uniquely identifies this Process in the Group, for
|
@@ -323,8 +320,7 @@ public:
|
|
323
320
|
/** Collected by Pool::collectAnalytics(). */
|
324
321
|
ProcessMetrics metrics;
|
325
322
|
|
326
|
-
Process(
|
327
|
-
pid_t _pid,
|
323
|
+
Process(pid_t _pid,
|
328
324
|
const string &_gupid,
|
329
325
|
const string &_connectPassword,
|
330
326
|
const FileDescriptor &_adminSocket,
|
@@ -338,7 +334,6 @@ public:
|
|
338
334
|
unsigned long long _spawnStartTime,
|
339
335
|
const SpawnerConfigPtr &_config = SpawnerConfigPtr())
|
340
336
|
: pqHandle(NULL),
|
341
|
-
libev(_libev.get()),
|
342
337
|
pid(_pid),
|
343
338
|
stickySessionId(0),
|
344
339
|
gupid(_gupid),
|
@@ -646,6 +641,7 @@ public:
|
|
646
641
|
stream << "<spawn_start_time>" << spawnStartTime << "</spawn_start_time>";
|
647
642
|
stream << "<spawn_end_time>" << spawnEndTime << "</spawn_end_time>";
|
648
643
|
stream << "<last_used>" << lastUsed << "</last_used>";
|
644
|
+
stream << "<last_used_desc>" << distanceOfTimeInWords(lastUsed / 1000000).c_str() << " ago</last_used_desc>";
|
649
645
|
stream << "<uptime>" << uptime() << "</uptime>";
|
650
646
|
if (!codeRevision.empty()) {
|
651
647
|
stream << "<code_revision>" << codeRevision << "</code_revision>";
|
@@ -54,43 +54,3 @@ The `Pool` class's `get` method is the main interface into the ApplicationPool2
|
|
54
54
|
subsystem. When an HTTP request comes in, call `Pool::get()` with the
|
55
55
|
appropriate arguments, and it will automatically spawn a process for you when
|
56
56
|
needed, open a session with that process and give you the session object.
|
57
|
-
|
58
|
-
|
59
|
-
## Threading notes
|
60
|
-
|
61
|
-
ApplicationPool2 depends on an event loop for handling timers and I/O. The I/O
|
62
|
-
that it handles is not the request/response I/O with application processes, but
|
63
|
-
things like forwarding the processes' stderr output to our stderr. In order not
|
64
|
-
to block the event loop with long-running operations, it uses a lot of
|
65
|
-
background threads. ApplicationPool2 is designed to be entirely thread-safe.
|
66
|
-
That said, if one's not careful, one may cause deadlocks, so read this section
|
67
|
-
carefully.
|
68
|
-
|
69
|
-
* Many Spawner methods are blocking because they wait for a subprocess to do
|
70
|
-
something (initializing, shutting down, etc). The process may output I/O
|
71
|
-
which is supposed to be handled by the main loop. If the event loop is blocked
|
72
|
-
on waiting for the process, and the process is blocked on a write() to the I/O
|
73
|
-
channel, then we have a deadlock. Therefore Spawner methods (including the
|
74
|
-
Spawner destructor) must always be called outside the event loop thread, and
|
75
|
-
the event loop must be available while the Spawner is doing its work. The only
|
76
|
-
exceptions are Spawner methods which are explicitly documented as not
|
77
|
-
depending on the event loop.
|
78
|
-
|
79
|
-
Pool must only call Spawner methods from background threads. There's still a
|
80
|
-
caveat though: Pool's destructor waits for all background threads to finish.
|
81
|
-
Therefore one must not destroy Pool from the event loop. Instead, I recommend
|
82
|
-
running the event loop in a separate thread, destroy Pool from the main
|
83
|
-
thread, and stop the event loop after Pool is destroyed.
|
84
|
-
|
85
|
-
Calling other Pool methods from the event loop is ok. Calling SpawnerFactory
|
86
|
-
from the event loop is ok.
|
87
|
-
|
88
|
-
* Many classes contain libev watchers and unregisters them in their destructor.
|
89
|
-
In order for this unregistration to succeed, one of the following conditions
|
90
|
-
must hold:
|
91
|
-
1. The destructor is called from the event loop.
|
92
|
-
2. The destructor is not called from the event loop, but the event loop is
|
93
|
-
still running.
|
94
|
-
|
95
|
-
Therefore I recommend that you destroy all ApplicationPool2-related objects
|
96
|
-
before stopping the event loop thread.
|