passenger 5.0.9 → 5.0.10
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 +15 -0
- data/CONTRIBUTORS +6 -0
- data/README.md +1 -1
- data/bin/passenger-install-apache2-module +24 -11
- data/bin/passenger-status +29 -14
- data/build/agents.rb +12 -10
- data/build/cxx_tests.rb +30 -30
- data/doc/Design and Architecture.html +1 -10
- data/doc/Design and Architecture.txt +1 -6
- data/doc/Users guide Apache.html +1 -19
- data/doc/Users guide Apache.txt +1 -1
- data/doc/Users guide Nginx.html +2 -20
- data/doc/Users guide Nginx.txt +2 -2
- data/doc/users_guide_snippets/tips.txt +0 -9
- data/ext/common/ApplicationPool2/ApiKey.h +158 -0
- data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
- data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
- data/ext/common/ApplicationPool2/Common.h +5 -44
- data/ext/common/ApplicationPool2/Context.h +94 -0
- data/ext/common/ApplicationPool2/Group.h +130 -1205
- data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
- data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
- data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
- data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
- data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
- data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
- data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
- data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
- data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
- data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
- data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
- data/ext/common/ApplicationPool2/Options.h +5 -5
- data/ext/common/ApplicationPool2/Pool.h +260 -815
- data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
- data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
- data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
- data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
- data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
- data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
- data/ext/common/ApplicationPool2/Process.h +399 -205
- data/ext/common/ApplicationPool2/Session.h +70 -28
- data/ext/common/ApplicationPool2/Socket.h +1 -0
- data/ext/common/Constants.h +11 -3
- data/ext/common/Exceptions.h +1 -1
- data/ext/common/Logging.cpp +9 -4
- data/ext/common/Logging.h +6 -0
- data/ext/common/ServerKit/HttpServer.h +225 -215
- data/ext/common/ServerKit/Server.h +57 -57
- data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
- data/ext/common/SpawningKit/Config.h +107 -0
- data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
- data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
- data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
- data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
- data/ext/common/SpawningKit/PipeWatcher.h +148 -0
- data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
- data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
- data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
- data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
- data/ext/common/Utils/BufferedIO.h +24 -0
- data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
- data/ext/common/Utils/IOUtils.cpp +70 -0
- data/ext/common/Utils/IOUtils.h +19 -0
- data/ext/common/Utils/JsonUtils.h +113 -0
- data/ext/common/Utils/StrIntUtils.h +29 -0
- data/ext/common/Utils/json.h +1 -1
- data/ext/common/agents/ApiServerUtils.h +941 -0
- data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
- data/ext/common/agents/HelperAgent/Main.cpp +86 -88
- data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
- data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
- data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
- data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
- data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
- data/ext/common/agents/Watchdog/ApiServer.h +311 -0
- data/ext/common/agents/Watchdog/Main.cpp +91 -65
- data/helper-scripts/prespawn +2 -0
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
- data/lib/phusion_passenger/common_library.rb +27 -14
- data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
- data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
- data/lib/phusion_passenger/config/main.rb +3 -5
- data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
- data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
- data/lib/phusion_passenger/config/utils.rb +15 -8
- data/lib/phusion_passenger/constants.rb +6 -2
- data/lib/phusion_passenger/platform_info/apache.rb +4 -0
- data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
- data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
- metadata +42 -21
- metadata.gz.asc +7 -7
- data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
- data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
- data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
- data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
- data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
- data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2011-
|
3
|
+
* Copyright (c) 2011-2015 Phusion
|
4
4
|
*
|
5
5
|
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
6
|
*
|
@@ -38,6 +38,7 @@
|
|
38
38
|
#include <UnionStation/Core.h>
|
39
39
|
#include <UnionStation/Transaction.h>
|
40
40
|
#include <ApplicationPool2/Options.h>
|
41
|
+
#include <SpawningKit/Config.h>
|
41
42
|
#include <Utils/VariantMap.h>
|
42
43
|
|
43
44
|
namespace tut {
|
@@ -52,7 +53,6 @@ using namespace boost;
|
|
52
53
|
using namespace oxt;
|
53
54
|
|
54
55
|
class Pool;
|
55
|
-
class SuperGroup;
|
56
56
|
class Group;
|
57
57
|
class Process;
|
58
58
|
class Socket;
|
@@ -161,12 +161,11 @@ enum RestartMethod {
|
|
161
161
|
};
|
162
162
|
|
163
163
|
typedef boost::shared_ptr<Pool> PoolPtr;
|
164
|
-
typedef boost::shared_ptr<SuperGroup> SuperGroupPtr;
|
165
164
|
typedef boost::shared_ptr<Group> GroupPtr;
|
166
|
-
typedef boost::
|
165
|
+
typedef boost::intrusive_ptr<Process> ProcessPtr;
|
167
166
|
typedef boost::intrusive_ptr<Session> SessionPtr;
|
168
167
|
typedef boost::shared_ptr<tracable_exception> ExceptionPtr;
|
169
|
-
typedef StringKeyTable<
|
168
|
+
typedef StringKeyTable<GroupPtr> GroupMap;
|
170
169
|
typedef boost::function<void (const ProcessPtr &process, DisableResult result)> DisableCallback;
|
171
170
|
typedef boost::function<void ()> Callback;
|
172
171
|
|
@@ -202,48 +201,10 @@ struct Ticket {
|
|
202
201
|
ExceptionPtr exception;
|
203
202
|
};
|
204
203
|
|
205
|
-
struct SpawnerConfig {
|
206
|
-
// Used by error pages and hooks.
|
207
|
-
ResourceLocator *resourceLocator;
|
208
|
-
const VariantMap *agentsOptions;
|
209
|
-
|
210
|
-
// Used for Union Station logging.
|
211
|
-
UnionStation::CorePtr unionStationCore;
|
212
|
-
|
213
|
-
// Used by SmartSpawner and DirectSpawner.
|
214
|
-
RandomGeneratorPtr randomGenerator;
|
215
|
-
string instanceDir;
|
216
|
-
|
217
|
-
// Used by DummySpawner and SpawnerFactory.
|
218
|
-
unsigned int concurrency;
|
219
|
-
unsigned int spawnerCreationSleepTime;
|
220
|
-
unsigned int spawnTime;
|
221
|
-
|
222
|
-
SpawnerConfig()
|
223
|
-
: resourceLocator(NULL),
|
224
|
-
agentsOptions(NULL),
|
225
|
-
concurrency(1),
|
226
|
-
spawnerCreationSleepTime(0),
|
227
|
-
spawnTime(0)
|
228
|
-
{ }
|
229
|
-
|
230
|
-
void finalize() {
|
231
|
-
TRACE_POINT();
|
232
|
-
if (resourceLocator == NULL) {
|
233
|
-
throw RuntimeException("ResourceLocator not initialized");
|
234
|
-
}
|
235
|
-
if (randomGenerator == NULL) {
|
236
|
-
randomGenerator = boost::make_shared<RandomGenerator>();
|
237
|
-
}
|
238
|
-
}
|
239
|
-
};
|
240
|
-
|
241
|
-
typedef boost::shared_ptr<SpawnerConfig> SpawnerConfigPtr;
|
242
|
-
|
243
204
|
ExceptionPtr copyException(const tracable_exception &e);
|
244
205
|
void rethrowException(const ExceptionPtr &e);
|
245
206
|
void processAndLogNewSpawnException(SpawnException &e, const Options &options,
|
246
|
-
const
|
207
|
+
const SpawningKit::ConfigPtr &config);
|
247
208
|
void recreateString(psg_pool_t *pool, StaticString &str);
|
248
209
|
|
249
210
|
} // namespace ApplicationPool2
|
@@ -0,0 +1,94 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2014-2015 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#ifndef _PASSENGER_APPLICATION_POOL2_CONTEXT_H_
|
26
|
+
#define _PASSENGER_APPLICATION_POOL2_CONTEXT_H_
|
27
|
+
|
28
|
+
#include <boost/thread.hpp>
|
29
|
+
#include <boost/pool/object_pool.hpp>
|
30
|
+
#include <Exceptions.h>
|
31
|
+
#include <SpawningKit/Factory.h>
|
32
|
+
#include <Utils/ClassUtils.h>
|
33
|
+
|
34
|
+
namespace Passenger {
|
35
|
+
namespace ApplicationPool2 {
|
36
|
+
|
37
|
+
|
38
|
+
using namespace boost;
|
39
|
+
|
40
|
+
class Session;
|
41
|
+
class Process;
|
42
|
+
|
43
|
+
|
44
|
+
/**
|
45
|
+
* State shared by Pool, Group, Process and Session. It contains statistics
|
46
|
+
* and counters, memory management objects, configuration objects, etc.
|
47
|
+
* This struct was introduced so that Group, Process and Sessions don't have
|
48
|
+
* to depend on Pool (which introduces circular dependencies).
|
49
|
+
*
|
50
|
+
* The fields are separated in several groups. Each group may have its own mutex.
|
51
|
+
* If it does, then all operations on any of the fields in that group requires
|
52
|
+
* grabbing the mutex unless documented otherwise.
|
53
|
+
*/
|
54
|
+
class Context {
|
55
|
+
private:
|
56
|
+
/****** Memory management objects *****/
|
57
|
+
|
58
|
+
P_RO_PROPERTY_REF(private, boost::mutex, MmSyncher);
|
59
|
+
P_RO_PROPERTY_REF(private, object_pool<Session>, SessionObjectPool);
|
60
|
+
P_RO_PROPERTY_REF(private, object_pool<Process>, ProcessObjectPool);
|
61
|
+
|
62
|
+
|
63
|
+
/****** Configuration objects ******/
|
64
|
+
|
65
|
+
P_PROPERTY_CONST_REF(private, SpawningKit::FactoryPtr, SpawningKitFactory);
|
66
|
+
|
67
|
+
|
68
|
+
public:
|
69
|
+
/****** Initialization ******/
|
70
|
+
|
71
|
+
Context()
|
72
|
+
: mSessionObjectPool(64, 1024),
|
73
|
+
mProcessObjectPool(4, 64)
|
74
|
+
{ }
|
75
|
+
|
76
|
+
void finalize() {
|
77
|
+
if (mSpawningKitFactory == NULL) {
|
78
|
+
throw RuntimeException("spawningKitFactory must be set");
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
/****** Configuration objects ******/
|
84
|
+
|
85
|
+
const SpawningKit::ConfigPtr &getSpawningKitConfig() const {
|
86
|
+
return mSpawningKitFactory->getConfig();
|
87
|
+
}
|
88
|
+
};
|
89
|
+
|
90
|
+
|
91
|
+
} // namespace ApplicationPool2
|
92
|
+
} // namespace Passenger
|
93
|
+
|
94
|
+
#endif /* _PASSENGER_APPLICATION_POOL2_CONTEXT_H_ */
|
@@ -39,14 +39,17 @@
|
|
39
39
|
#include <oxt/macros.hpp>
|
40
40
|
#include <oxt/thread.hpp>
|
41
41
|
#include <oxt/dynamic_thread_group.hpp>
|
42
|
+
#include <sys/types.h>
|
42
43
|
#include <sys/stat.h>
|
43
44
|
#include <cstdlib>
|
44
45
|
#include <cassert>
|
45
46
|
#include <ApplicationPool2/Common.h>
|
46
|
-
#include <ApplicationPool2/
|
47
|
-
#include <ApplicationPool2/
|
47
|
+
#include <ApplicationPool2/Context.h>
|
48
|
+
#include <ApplicationPool2/BasicGroupInfo.h>
|
48
49
|
#include <ApplicationPool2/Process.h>
|
49
50
|
#include <ApplicationPool2/Options.h>
|
51
|
+
#include <SpawningKit/Factory.h>
|
52
|
+
#include <SpawningKit/UserSwitchingRules.h>
|
50
53
|
#include <MemoryKit/palloc.h>
|
51
54
|
#include <Hooks.h>
|
52
55
|
#include <Utils.h>
|
@@ -68,7 +71,6 @@ class Group: public boost::enable_shared_from_this<Group> {
|
|
68
71
|
// Actually private, but marked public so that unit tests can access the fields.
|
69
72
|
public:
|
70
73
|
friend class Pool;
|
71
|
-
friend class SuperGroup;
|
72
74
|
|
73
75
|
struct GetAction {
|
74
76
|
GetCallback callback;
|
@@ -98,7 +100,7 @@ public:
|
|
98
100
|
enum LifeStatus {
|
99
101
|
/** Up and operational. */
|
100
102
|
ALIVE,
|
101
|
-
/** Being shut down. The containing
|
103
|
+
/** Being shut down. The containing Pool has issued the shutdown()
|
102
104
|
* command, and this Group is now waiting for all detached processes to
|
103
105
|
* exit. You cannot call `get()`, `restart()` and other mutating methods
|
104
106
|
* anymore, and all threads created by this Group will exit as soon
|
@@ -112,13 +114,15 @@ public:
|
|
112
114
|
SHUT_DOWN
|
113
115
|
};
|
114
116
|
|
117
|
+
BasicGroupInfo info;
|
118
|
+
|
115
119
|
/**
|
116
|
-
* A back reference to the containing
|
117
|
-
* be NULL because a
|
120
|
+
* A back reference to the containing Pool. Should never
|
121
|
+
* be NULL because a Pool should outlive all its containing
|
118
122
|
* Groups.
|
119
123
|
* Read-only; only set during initialization.
|
120
124
|
*/
|
121
|
-
|
125
|
+
Pool *pool;
|
122
126
|
time_t lastRestartFileMtime;
|
123
127
|
time_t lastRestartFileCheckTime;
|
124
128
|
|
@@ -185,16 +189,54 @@ public:
|
|
185
189
|
GroupPtr selfPointer;
|
186
190
|
|
187
191
|
|
188
|
-
|
189
|
-
|
192
|
+
/****** Initialization and shutdown ******/
|
193
|
+
|
194
|
+
static ApiKey generateApiKey(const Pool *pool);
|
195
|
+
static string generateUuid(const Pool *pool);
|
196
|
+
|
197
|
+
bool shutdownCanFinish() const;
|
198
|
+
void finishShutdown(boost::container::vector<Callback> &postLockActions);
|
199
|
+
|
200
|
+
/****** Session management ******/
|
201
|
+
|
202
|
+
RouteResult route(const Options &options) const;
|
203
|
+
SessionPtr newSession(Process *process, unsigned long long now = 0);
|
190
204
|
static void _onSessionInitiateFailure(Session *session);
|
191
205
|
static void _onSessionClose(Session *session);
|
192
206
|
OXT_FORCE_INLINE void onSessionInitiateFailure(Process *process, Session *session);
|
193
207
|
OXT_FORCE_INLINE void onSessionClose(Process *process, Session *session);
|
194
208
|
|
195
|
-
|
209
|
+
/****** Spawning and restarting ******/
|
210
|
+
|
211
|
+
void spawnThreadMain(GroupPtr self, SpawningKit::SpawnerPtr spawner, Options options,
|
212
|
+
unsigned int restartsInitiated);
|
213
|
+
void spawnThreadRealMain(const SpawningKit::SpawnerPtr &spawner, const Options &options,
|
214
|
+
unsigned int restartsInitiated);
|
215
|
+
void finalizeRestart(GroupPtr self, Options oldOptions, Options newOptions,
|
216
|
+
RestartMethod method, SpawningKit::FactoryPtr spawningKitFactory,
|
217
|
+
unsigned int restartsInitiated, boost::container::vector<Callback> postLockActions);
|
218
|
+
|
219
|
+
/****** Process list management ******/
|
220
|
+
|
221
|
+
Process *findProcessWithStickySessionId(unsigned int id) const;
|
222
|
+
Process *findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const;
|
223
|
+
Process *findProcessWithLowestBusyness(const ProcessList &processes) const;
|
224
|
+
Process *findEnabledProcessWithLowestBusyness() const;
|
225
|
+
|
226
|
+
void addProcessToList(const ProcessPtr &process, ProcessList &destination);
|
227
|
+
void removeProcessFromList(const ProcessPtr &process, ProcessList &source);
|
228
|
+
void removeFromDisableWaitlist(const ProcessPtr &p, DisableResult result,
|
229
|
+
boost::container::vector<Callback> &postLockActions);
|
230
|
+
void clearDisableWaitlist(DisableResult result,
|
231
|
+
boost::container::vector<Callback> &postLockActions);
|
232
|
+
void enableAllDisablingProcesses(boost::container::vector<Callback> &postLockActions);
|
233
|
+
|
234
|
+
void startCheckingDetachedProcesses(bool immediately);
|
235
|
+
void detachedProcessesCheckerMain(GroupPtr self);
|
236
|
+
|
237
|
+
/****** Out-of-band work ******/
|
238
|
+
|
196
239
|
bool oobwAllowed() const;
|
197
|
-
/** Returns whether a new OOBW should be initiated for this process. */
|
198
240
|
bool shouldInitiateOobw(Process *process) const;
|
199
241
|
void maybeInitiateOobw(Process *process);
|
200
242
|
void lockAndMaybeInitiateOobw(const ProcessPtr &process, DisableResult result, GroupPtr self);
|
@@ -202,581 +244,50 @@ public:
|
|
202
244
|
void spawnThreadOOBWRequest(GroupPtr self, ProcessPtr process);
|
203
245
|
void initiateNextOobwRequest();
|
204
246
|
|
205
|
-
|
206
|
-
|
207
|
-
void
|
208
|
-
|
209
|
-
void
|
210
|
-
|
211
|
-
|
212
|
-
void
|
213
|
-
|
214
|
-
|
215
|
-
|
247
|
+
/****** Internal utilities ******/
|
248
|
+
|
249
|
+
static void runAllActions(const boost::container::vector<Callback> &actions);
|
250
|
+
static void interruptAndJoinAllThreads(GroupPtr self);
|
251
|
+
static void doCleanupSpawner(SpawningKit::SpawnerPtr spawner);
|
252
|
+
|
253
|
+
void resetOptions(const Options &newOptions, Options *destination = NULL);
|
254
|
+
void mergeOptions(const Options &other);
|
255
|
+
|
256
|
+
bool prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name);
|
257
|
+
void runAttachHooks(const ProcessPtr process) const;
|
258
|
+
void runDetachHooks(const ProcessPtr process) const;
|
259
|
+
void setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const;
|
260
|
+
|
261
|
+
unsigned int generateStickySessionId();
|
262
|
+
ProcessPtr createProcessObject(const Json::Value &json);
|
216
263
|
bool poolAtFullCapacity() const;
|
264
|
+
ProcessPtr poolForceFreeCapacity(const Group *exclude, boost::container::vector<Callback> &postLockActions);
|
265
|
+
void wakeUpGarbageCollector();
|
217
266
|
bool anotherGroupIsWaitingForCapacity() const;
|
218
267
|
Group *findOtherGroupWaitingForCapacity() const;
|
219
|
-
|
268
|
+
bool pushGetWaiter(const Options &newOptions, const GetCallback &callback,
|
269
|
+
boost::container::vector<Callback> &postLockActions);
|
270
|
+
template<typename Lock> void assignSessionsToGetWaitersQuickly(Lock &lock);
|
271
|
+
void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions);
|
220
272
|
bool testOverflowRequestQueue() const;
|
221
273
|
void callAbortLongRunningConnectionsCallback(const ProcessPtr &process);
|
222
|
-
psg_pool_t *getPallocPool() const;
|
223
|
-
const ResourceLocator &getResourceLocator() const;
|
224
|
-
bool prepareHookScriptOptions(HookScriptOptions &hsOptions, const char *name);
|
225
|
-
void runAttachHooks(const ProcessPtr process) const;
|
226
|
-
void runDetachHooks(const ProcessPtr process) const;
|
227
|
-
void setupAttachOrDetachHook(const ProcessPtr process, HookScriptOptions &options) const;
|
228
274
|
|
229
|
-
|
230
|
-
// !a || b: logical equivalent of a IMPLIES b.
|
231
|
-
#ifndef NDEBUG
|
232
|
-
if (!selfCheckingEnabled()) {
|
233
|
-
return;
|
234
|
-
}
|
235
|
-
|
236
|
-
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
237
|
-
|
238
|
-
assert(enabledCount >= 0);
|
239
|
-
assert(disablingCount >= 0);
|
240
|
-
assert(disabledCount >= 0);
|
241
|
-
assert(nEnabledProcessesTotallyBusy >= 0);
|
242
|
-
assert(!( enabledCount == 0 && disablingCount > 0 ) || ( processesBeingSpawned > 0) );
|
243
|
-
assert(!( !m_spawning ) || ( enabledCount > 0 || disablingCount == 0 ));
|
244
|
-
|
245
|
-
assert((lifeStatus == ALIVE) == (spawner != NULL));
|
246
|
-
|
247
|
-
// Verify getWaitlist invariants.
|
248
|
-
assert(!( !getWaitlist.empty() ) || ( enabledProcesses.empty() || verifyNoRequestsOnGetWaitlistAreRoutable() ));
|
249
|
-
assert(!( enabledProcesses.empty() && !m_spawning && !restarting() && !poolAtFullCapacity() ) || ( getWaitlist.empty() ));
|
250
|
-
assert(!( !getWaitlist.empty() ) || ( !enabledProcesses.empty() || m_spawning || restarting() || poolAtFullCapacity() ));
|
251
|
-
|
252
|
-
// Verify disableWaitlist invariants.
|
253
|
-
assert((int) disableWaitlist.size() >= disablingCount);
|
254
|
-
|
255
|
-
// Verify processesBeingSpawned, m_spawning and m_restarting.
|
256
|
-
assert(!( processesBeingSpawned > 0 ) || ( m_spawning ));
|
257
|
-
assert(!( m_restarting ) || ( processesBeingSpawned == 0 ));
|
258
|
-
|
259
|
-
// Verify lifeStatus.
|
260
|
-
if (lifeStatus != ALIVE) {
|
261
|
-
assert(enabledCount == 0);
|
262
|
-
assert(disablingCount == 0);
|
263
|
-
assert(disabledCount == 0);
|
264
|
-
assert(nEnabledProcessesTotallyBusy == 0);
|
265
|
-
}
|
266
|
-
|
267
|
-
// Verify list sizes.
|
268
|
-
assert((int) enabledProcesses.size() == enabledCount);
|
269
|
-
assert((int) disablingProcesses.size() == disablingCount);
|
270
|
-
assert((int) disabledProcesses.size() == disabledCount);
|
271
|
-
assert(nEnabledProcessesTotallyBusy <= enabledCount);
|
272
|
-
#endif
|
273
|
-
}
|
274
|
-
|
275
|
-
void verifyExpensiveInvariants() const {
|
276
|
-
#ifndef NDEBUG
|
277
|
-
// !a || b: logical equivalent of a IMPLIES b.
|
278
|
-
|
279
|
-
if (!selfCheckingEnabled()) {
|
280
|
-
return;
|
281
|
-
}
|
282
|
-
|
283
|
-
ProcessList::const_iterator it, end;
|
284
|
-
|
285
|
-
end = enabledProcesses.end();
|
286
|
-
for (it = enabledProcesses.begin(); it != end; it++) {
|
287
|
-
const ProcessPtr &process = *it;
|
288
|
-
assert(process->enabled == Process::ENABLED);
|
289
|
-
assert(process->isAlive());
|
290
|
-
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
291
|
-
|| process->oobwStatus == Process::OOBW_REQUESTED);
|
292
|
-
}
|
293
|
-
|
294
|
-
end = disablingProcesses.end();
|
295
|
-
for (it = disablingProcesses.begin(); it != end; it++) {
|
296
|
-
const ProcessPtr &process = *it;
|
297
|
-
assert(process->enabled == Process::DISABLING);
|
298
|
-
assert(process->isAlive());
|
299
|
-
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
300
|
-
|| process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
301
|
-
}
|
302
|
-
|
303
|
-
end = disabledProcesses.end();
|
304
|
-
for (it = disabledProcesses.begin(); it != end; it++) {
|
305
|
-
const ProcessPtr &process = *it;
|
306
|
-
assert(process->enabled == Process::DISABLED);
|
307
|
-
assert(process->isAlive());
|
308
|
-
assert(process->oobwStatus == Process::OOBW_NOT_ACTIVE
|
309
|
-
|| process->oobwStatus == Process::OOBW_IN_PROGRESS);
|
310
|
-
}
|
311
|
-
|
312
|
-
foreach (const ProcessPtr &process, detachedProcesses) {
|
313
|
-
assert(process->enabled == Process::DETACHED);
|
314
|
-
}
|
315
|
-
#endif
|
316
|
-
}
|
275
|
+
/****** Correctness verification ******/
|
317
276
|
|
277
|
+
bool selfCheckingEnabled() const;
|
278
|
+
void verifyInvariants() const;
|
279
|
+
void verifyExpensiveInvariants() const;
|
318
280
|
#ifndef NDEBUG
|
319
|
-
|
320
|
-
deque<GetWaiter>::const_iterator it, end = getWaitlist.end();
|
321
|
-
|
322
|
-
for (it = getWaitlist.begin(); it != end; it++) {
|
323
|
-
if (route(it->options).process != NULL) {
|
324
|
-
return false;
|
325
|
-
}
|
326
|
-
}
|
327
|
-
return true;
|
328
|
-
}
|
281
|
+
bool verifyNoRequestsOnGetWaitlistAreRoutable() const;
|
329
282
|
#endif
|
330
283
|
|
331
|
-
/**
|
332
|
-
* Persists options into this Group. Called at creation time and at restart time.
|
333
|
-
* Values will be persisted into `destination`. Or if it's NULL, into `this->options`.
|
334
|
-
*/
|
335
|
-
void resetOptions(const Options &newOptions, Options *destination = NULL) {
|
336
|
-
if (destination == NULL) {
|
337
|
-
destination = &this->options;
|
338
|
-
}
|
339
|
-
*destination = newOptions;
|
340
|
-
destination->persist(newOptions);
|
341
|
-
destination->clearPerRequestFields();
|
342
|
-
destination->groupSecret = StaticString(secret, SECRET_SIZE);
|
343
|
-
destination->groupUuid = uuid;
|
344
|
-
}
|
345
|
-
|
346
|
-
/**
|
347
|
-
* Merges some of the new options from the latest get() request into this Group.
|
348
|
-
*/
|
349
|
-
void mergeOptions(const Options &other) {
|
350
|
-
options.maxRequests = other.maxRequests;
|
351
|
-
options.minProcesses = other.minProcesses;
|
352
|
-
options.statThrottleRate = other.statThrottleRate;
|
353
|
-
options.maxPreloaderIdleTime = other.maxPreloaderIdleTime;
|
354
|
-
}
|
355
|
-
|
356
|
-
static void runAllActions(const boost::container::vector<Callback> &actions) {
|
357
|
-
boost::container::vector<Callback>::const_iterator it, end = actions.end();
|
358
|
-
for (it = actions.begin(); it != end; it++) {
|
359
|
-
(*it)();
|
360
|
-
}
|
361
|
-
}
|
362
|
-
|
363
|
-
static void doCleanupSpawner(SpawnerPtr spawner) {
|
364
|
-
spawner->cleanup();
|
365
|
-
}
|
366
|
-
|
367
|
-
unsigned int generateStickySessionId() {
|
368
|
-
unsigned int result;
|
369
|
-
|
370
|
-
while (true) {
|
371
|
-
result = (unsigned int) rand();
|
372
|
-
if (findProcessWithStickySessionId(result) == NULL) {
|
373
|
-
return result;
|
374
|
-
}
|
375
|
-
}
|
376
|
-
// Never reached; shut up compiler warning.
|
377
|
-
return 0;
|
378
|
-
}
|
379
|
-
|
380
|
-
/* Determines which process to route a get() action to. The returned process
|
381
|
-
* is guaranteed to be `canBeRoutedTo()`, i.e. not totally busy.
|
382
|
-
*
|
383
|
-
* A request is routed to an enabled processes, or if there are none,
|
384
|
-
* from a disabling process. The rationale is as follows:
|
385
|
-
* If there are no enabled process, then waiting for one to spawn is too
|
386
|
-
* expensive. The next best thing is to route to disabling processes
|
387
|
-
* until more processes have been spawned.
|
388
|
-
*/
|
389
|
-
RouteResult route(const Options &options) const {
|
390
|
-
if (OXT_LIKELY(enabledCount > 0)) {
|
391
|
-
if (options.stickySessionId == 0) {
|
392
|
-
Process *process = findEnabledProcessWithLowestBusyness();
|
393
|
-
if (process->canBeRoutedTo()) {
|
394
|
-
return RouteResult(process);
|
395
|
-
} else {
|
396
|
-
return RouteResult(NULL, true);
|
397
|
-
}
|
398
|
-
} else {
|
399
|
-
Process *process = findProcessWithStickySessionIdOrLowestBusyness(
|
400
|
-
options.stickySessionId);
|
401
|
-
if (process != NULL) {
|
402
|
-
if (process->canBeRoutedTo()) {
|
403
|
-
return RouteResult(process);
|
404
|
-
} else {
|
405
|
-
return RouteResult(NULL, false);
|
406
|
-
}
|
407
|
-
} else {
|
408
|
-
return RouteResult(NULL, true);
|
409
|
-
}
|
410
|
-
}
|
411
|
-
} else {
|
412
|
-
Process *process = findProcessWithLowestBusyness(disablingProcesses);
|
413
|
-
if (process->canBeRoutedTo()) {
|
414
|
-
return RouteResult(process);
|
415
|
-
} else {
|
416
|
-
return RouteResult(NULL, true);
|
417
|
-
}
|
418
|
-
}
|
419
|
-
}
|
420
|
-
|
421
|
-
SessionPtr newSession(Process *process, unsigned long long now = 0) {
|
422
|
-
bool wasTotallyBusy = process->isTotallyBusy();
|
423
|
-
SessionPtr session = process->newSession(now);
|
424
|
-
session->onInitiateFailure = _onSessionInitiateFailure;
|
425
|
-
session->onClose = _onSessionClose;
|
426
|
-
if (process->enabled == Process::ENABLED) {
|
427
|
-
enabledProcessBusynessLevels[process->index] = process->busyness();
|
428
|
-
if (!wasTotallyBusy && process->isTotallyBusy()) {
|
429
|
-
nEnabledProcessesTotallyBusy++;
|
430
|
-
}
|
431
|
-
}
|
432
|
-
return session;
|
433
|
-
}
|
434
|
-
|
435
|
-
bool pushGetWaiter(const Options &newOptions, const GetCallback &callback,
|
436
|
-
boost::container::vector<Callback> &postLockActions)
|
437
|
-
{
|
438
|
-
if (OXT_LIKELY(!testOverflowRequestQueue()
|
439
|
-
&& (newOptions.maxRequestQueueSize == 0
|
440
|
-
|| getWaitlist.size() < newOptions.maxRequestQueueSize)))
|
441
|
-
{
|
442
|
-
getWaitlist.push_back(GetWaiter(
|
443
|
-
newOptions.copyAndPersist().detachFromUnionStationTransaction(),
|
444
|
-
callback));
|
445
|
-
return true;
|
446
|
-
} else {
|
447
|
-
postLockActions.push_back(boost::bind(GetCallback::call,
|
448
|
-
callback, SessionPtr(), boost::make_shared<RequestQueueFullException>(newOptions.maxRequestQueueSize)));
|
449
|
-
|
450
|
-
HookScriptOptions hsOptions;
|
451
|
-
if (prepareHookScriptOptions(hsOptions, "queue_full_error")) {
|
452
|
-
// TODO <Feb 17, 2015] DK> should probably rate limit this, since we are already at heavy load
|
453
|
-
postLockActions.push_back(boost::bind(runHookScripts, hsOptions));
|
454
|
-
}
|
455
|
-
|
456
|
-
return false;
|
457
|
-
}
|
458
|
-
}
|
459
|
-
|
460
|
-
Process *findProcessWithStickySessionId(unsigned int id) const {
|
461
|
-
ProcessList::const_iterator it, end = enabledProcesses.end();
|
462
|
-
for (it = enabledProcesses.begin(); it != end; it++) {
|
463
|
-
Process *process = it->get();
|
464
|
-
if (process->stickySessionId == id) {
|
465
|
-
return process;
|
466
|
-
}
|
467
|
-
}
|
468
|
-
return NULL;
|
469
|
-
}
|
470
|
-
|
471
|
-
Process *findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const {
|
472
|
-
int leastBusyProcessIndex = -1;
|
473
|
-
int lowestBusyness = 0;
|
474
|
-
unsigned int i, size = enabledProcessBusynessLevels.size();
|
475
|
-
const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];
|
476
|
-
|
477
|
-
for (i = 0; i < size; i++) {
|
478
|
-
Process *process = enabledProcesses[i].get();
|
479
|
-
if (process->stickySessionId == id) {
|
480
|
-
return process;
|
481
|
-
} else if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
|
482
|
-
leastBusyProcessIndex = i;
|
483
|
-
lowestBusyness = enabledProcessBusynessLevels[i];
|
484
|
-
}
|
485
|
-
}
|
486
|
-
|
487
|
-
if (leastBusyProcessIndex == -1) {
|
488
|
-
return NULL;
|
489
|
-
} else {
|
490
|
-
return enabledProcesses[leastBusyProcessIndex].get();
|
491
|
-
}
|
492
|
-
}
|
493
|
-
|
494
|
-
Process *findProcessWithLowestBusyness(const ProcessList &processes) const {
|
495
|
-
if (processes.empty()) {
|
496
|
-
return NULL;
|
497
|
-
}
|
498
|
-
|
499
|
-
int lowestBusyness = -1;
|
500
|
-
Process *leastBusyProcess = NULL;
|
501
|
-
ProcessList::const_iterator it;
|
502
|
-
ProcessList::const_iterator end = processes.end();
|
503
|
-
for (it = processes.begin(); it != end; it++) {
|
504
|
-
Process *process = (*it).get();
|
505
|
-
int busyness = process->busyness();
|
506
|
-
if (lowestBusyness == -1 || lowestBusyness > busyness) {
|
507
|
-
lowestBusyness = busyness;
|
508
|
-
leastBusyProcess = process;
|
509
|
-
}
|
510
|
-
}
|
511
|
-
return leastBusyProcess;
|
512
|
-
}
|
513
|
-
|
514
|
-
/**
|
515
|
-
* Cache-optimized version of findProcessWithLowestBusyness() for the common case.
|
516
|
-
*/
|
517
|
-
Process *findEnabledProcessWithLowestBusyness() const {
|
518
|
-
if (enabledProcesses.empty()) {
|
519
|
-
return NULL;
|
520
|
-
}
|
521
|
-
|
522
|
-
int leastBusyProcessIndex = -1;
|
523
|
-
int lowestBusyness = 0;
|
524
|
-
unsigned int i, size = enabledProcessBusynessLevels.size();
|
525
|
-
const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];
|
526
|
-
|
527
|
-
for (i = 0; i < size; i++) {
|
528
|
-
if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
|
529
|
-
leastBusyProcessIndex = i;
|
530
|
-
lowestBusyness = enabledProcessBusynessLevels[i];
|
531
|
-
}
|
532
|
-
}
|
533
|
-
return enabledProcesses[leastBusyProcessIndex].get();
|
534
|
-
}
|
535
|
-
|
536
|
-
/**
|
537
|
-
* Removes a process to the given list (enabledProcess, disablingProcesses, disabledProcesses).
|
538
|
-
* This function does not fix getWaitlist invariants or other stuff.
|
539
|
-
*/
|
540
|
-
void removeProcessFromList(const ProcessPtr &process, ProcessList &source) {
|
541
|
-
ProcessPtr p = process; // Keep an extra reference count just in case.
|
542
|
-
|
543
|
-
source.erase(source.begin() + process->index);
|
544
|
-
process->index = -1;
|
545
|
-
|
546
|
-
switch (process->enabled) {
|
547
|
-
case Process::ENABLED:
|
548
|
-
assert(&source == &enabledProcesses);
|
549
|
-
enabledCount--;
|
550
|
-
if (process->isTotallyBusy()) {
|
551
|
-
nEnabledProcessesTotallyBusy--;
|
552
|
-
}
|
553
|
-
break;
|
554
|
-
case Process::DISABLING:
|
555
|
-
assert(&source == &disablingProcesses);
|
556
|
-
disablingCount--;
|
557
|
-
break;
|
558
|
-
case Process::DISABLED:
|
559
|
-
assert(&source == &disabledProcesses);
|
560
|
-
disabledCount--;
|
561
|
-
break;
|
562
|
-
case Process::DETACHED:
|
563
|
-
assert(&source == &detachedProcesses);
|
564
|
-
break;
|
565
|
-
default:
|
566
|
-
P_BUG("Unknown 'enabled' state " << (int) process->enabled);
|
567
|
-
}
|
568
|
-
|
569
|
-
// Rebuild indices
|
570
|
-
ProcessList::iterator it, end = source.end();
|
571
|
-
unsigned int i = 0;
|
572
|
-
for (it = source.begin(); it != end; it++, i++) {
|
573
|
-
const ProcessPtr &process = *it;
|
574
|
-
process->index = i;
|
575
|
-
}
|
576
|
-
|
577
|
-
// Rebuild enabledProcessBusynessLevels
|
578
|
-
if (&source == &enabledProcesses) {
|
579
|
-
enabledProcessBusynessLevels.clear();
|
580
|
-
for (it = source.begin(); it != end; it++, i++) {
|
581
|
-
const ProcessPtr &process = *it;
|
582
|
-
enabledProcessBusynessLevels.push_back(process->busyness());
|
583
|
-
}
|
584
|
-
enabledProcessBusynessLevels.shrink_to_fit();
|
585
|
-
}
|
586
|
-
}
|
587
|
-
|
588
|
-
/**
|
589
|
-
* Adds a process to the given list (enabledProcess, disablingProcesses, disabledProcesses)
|
590
|
-
* and sets the process->enabled flag accordingly.
|
591
|
-
* The process must currently not be in any list. This function does not fix
|
592
|
-
* getWaitlist invariants or other stuff.
|
593
|
-
*/
|
594
|
-
void addProcessToList(const ProcessPtr &process, ProcessList &destination) {
|
595
|
-
destination.push_back(process);
|
596
|
-
process->index = destination.size() - 1;
|
597
|
-
if (&destination == &enabledProcesses) {
|
598
|
-
process->enabled = Process::ENABLED;
|
599
|
-
enabledCount++;
|
600
|
-
enabledProcessBusynessLevels.push_back(process->busyness());
|
601
|
-
if (process->isTotallyBusy()) {
|
602
|
-
nEnabledProcessesTotallyBusy++;
|
603
|
-
}
|
604
|
-
} else if (&destination == &disablingProcesses) {
|
605
|
-
process->enabled = Process::DISABLING;
|
606
|
-
disablingCount++;
|
607
|
-
} else if (&destination == &disabledProcesses) {
|
608
|
-
assert(process->sessions == 0);
|
609
|
-
process->enabled = Process::DISABLED;
|
610
|
-
disabledCount++;
|
611
|
-
} else if (&destination == &detachedProcesses) {
|
612
|
-
assert(process->isAlive());
|
613
|
-
process->enabled = Process::DETACHED;
|
614
|
-
callAbortLongRunningConnectionsCallback(process);
|
615
|
-
} else {
|
616
|
-
P_BUG("Unknown destination list");
|
617
|
-
}
|
618
|
-
}
|
619
|
-
|
620
|
-
template<typename Lock>
|
621
|
-
void assignSessionsToGetWaitersQuickly(Lock &lock) {
|
622
|
-
if (getWaitlist.empty()) {
|
623
|
-
verifyInvariants();
|
624
|
-
lock.unlock();
|
625
|
-
return;
|
626
|
-
}
|
627
|
-
|
628
|
-
SmallVector<GetAction, 8> actions;
|
629
|
-
unsigned int i = 0;
|
630
|
-
bool done = false;
|
631
|
-
|
632
|
-
actions.reserve(getWaitlist.size());
|
633
|
-
|
634
|
-
while (!done && i < getWaitlist.size()) {
|
635
|
-
const GetWaiter &waiter = getWaitlist[i];
|
636
|
-
RouteResult result = route(waiter.options);
|
637
|
-
if (result.process != NULL) {
|
638
|
-
GetAction action;
|
639
|
-
action.callback = waiter.callback;
|
640
|
-
action.session = newSession(result.process);
|
641
|
-
getWaitlist.erase(getWaitlist.begin() + i);
|
642
|
-
actions.push_back(action);
|
643
|
-
} else {
|
644
|
-
done = result.finished;
|
645
|
-
if (!result.finished) {
|
646
|
-
i++;
|
647
|
-
}
|
648
|
-
}
|
649
|
-
}
|
650
|
-
|
651
|
-
verifyInvariants();
|
652
|
-
lock.unlock();
|
653
|
-
SmallVector<GetAction, 50>::const_iterator it, end = actions.end();
|
654
|
-
for (it = actions.begin(); it != end; it++) {
|
655
|
-
it->callback(it->session, ExceptionPtr());
|
656
|
-
}
|
657
|
-
}
|
658
|
-
|
659
|
-
void assignSessionsToGetWaiters(boost::container::vector<Callback> &postLockActions) {
|
660
|
-
unsigned int i = 0;
|
661
|
-
bool done = false;
|
662
|
-
|
663
|
-
while (!done && i < getWaitlist.size()) {
|
664
|
-
const GetWaiter &waiter = getWaitlist[i];
|
665
|
-
RouteResult result = route(waiter.options);
|
666
|
-
if (result.process != NULL) {
|
667
|
-
postLockActions.push_back(boost::bind(
|
668
|
-
GetCallback::call,
|
669
|
-
waiter.callback,
|
670
|
-
newSession(result.process),
|
671
|
-
ExceptionPtr()));
|
672
|
-
getWaitlist.erase(getWaitlist.begin() + i);
|
673
|
-
} else {
|
674
|
-
done = result.finished;
|
675
|
-
if (!result.finished) {
|
676
|
-
i++;
|
677
|
-
}
|
678
|
-
}
|
679
|
-
}
|
680
|
-
}
|
681
|
-
|
682
|
-
void enableAllDisablingProcesses(boost::container::vector<Callback> &postLockActions) {
|
683
|
-
P_DEBUG("Enabling all DISABLING processes with result DR_ERROR");
|
684
|
-
deque<DisableWaiter>::iterator it, end = disableWaitlist.end();
|
685
|
-
for (it = disableWaitlist.begin(); it != end; it++) {
|
686
|
-
const DisableWaiter &waiter = *it;
|
687
|
-
const ProcessPtr process = waiter.process;
|
688
|
-
// A process can appear multiple times in disableWaitlist.
|
689
|
-
assert(process->enabled == Process::DISABLING
|
690
|
-
|| process->enabled == Process::ENABLED);
|
691
|
-
if (process->enabled == Process::DISABLING) {
|
692
|
-
removeProcessFromList(process, disablingProcesses);
|
693
|
-
addProcessToList(process, enabledProcesses);
|
694
|
-
P_DEBUG("Enabled process " << process->inspect());
|
695
|
-
}
|
696
|
-
}
|
697
|
-
clearDisableWaitlist(DR_ERROR, postLockActions);
|
698
|
-
}
|
699
|
-
|
700
|
-
void removeFromDisableWaitlist(const ProcessPtr &p, DisableResult result,
|
701
|
-
boost::container::vector<Callback> &postLockActions)
|
702
|
-
{
|
703
|
-
deque<DisableWaiter>::const_iterator it, end = disableWaitlist.end();
|
704
|
-
deque<DisableWaiter> newList;
|
705
|
-
for (it = disableWaitlist.begin(); it != end; it++) {
|
706
|
-
const DisableWaiter &waiter = *it;
|
707
|
-
const ProcessPtr process = waiter.process;
|
708
|
-
if (process == p) {
|
709
|
-
postLockActions.push_back(boost::bind(waiter.callback, p, result));
|
710
|
-
} else {
|
711
|
-
newList.push_back(waiter);
|
712
|
-
}
|
713
|
-
}
|
714
|
-
disableWaitlist = newList;
|
715
|
-
}
|
716
|
-
|
717
|
-
void clearDisableWaitlist(DisableResult result,
|
718
|
-
boost::container::vector<Callback> &postLockActions)
|
719
|
-
{
|
720
|
-
// This function may be called after processes in the disableWaitlist
|
721
|
-
// have been disabled or enabled, so do not assume any value for
|
722
|
-
// waiter.process->enabled in this function.
|
723
|
-
postLockActions.reserve(postLockActions.size() + disableWaitlist.size());
|
724
|
-
while (!disableWaitlist.empty()) {
|
725
|
-
const DisableWaiter &waiter = disableWaitlist.front();
|
726
|
-
postLockActions.push_back(boost::bind(waiter.callback, waiter.process, result));
|
727
|
-
disableWaitlist.pop_front();
|
728
|
-
}
|
729
|
-
}
|
730
|
-
|
731
|
-
bool shutdownCanFinish() const {
|
732
|
-
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
733
|
-
return lifeStatus == SHUTTING_DOWN
|
734
|
-
&& enabledCount == 0
|
735
|
-
&& disablingCount == 0
|
736
|
-
&& disabledCount == 0
|
737
|
-
&& detachedProcesses.empty();
|
738
|
-
}
|
739
|
-
|
740
|
-
static void interruptAndJoinAllThreads(GroupPtr self) {
|
741
|
-
self->interruptableThreads.interrupt_and_join_all();
|
742
|
-
}
|
743
|
-
|
744
|
-
/** One of the post lock actions can potentially perform a long-running
|
745
|
-
* operation, so running them in a thread is advised.
|
746
|
-
*/
|
747
|
-
void finishShutdown(boost::container::vector<Callback> &postLockActions) {
|
748
|
-
TRACE_POINT();
|
749
|
-
#ifndef NDEBUG
|
750
|
-
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
751
|
-
P_ASSERT_EQ(lifeStatus, SHUTTING_DOWN);
|
752
|
-
#endif
|
753
|
-
P_DEBUG("Finishing shutdown of group " << name);
|
754
|
-
if (shutdownCallback) {
|
755
|
-
postLockActions.push_back(shutdownCallback);
|
756
|
-
shutdownCallback = Callback();
|
757
|
-
}
|
758
|
-
postLockActions.push_back(boost::bind(interruptAndJoinAllThreads,
|
759
|
-
shared_from_this()));
|
760
|
-
this->lifeStatus.store(SHUT_DOWN, boost::memory_order_release);
|
761
|
-
selfPointer.reset();
|
762
|
-
}
|
763
|
-
|
764
284
|
public:
|
765
|
-
static const unsigned int SECRET_SIZE = 16;
|
766
|
-
|
767
285
|
Options options;
|
768
|
-
/** This name uniquely identifies this Group within its Pool. It can also be used as the display name. */
|
769
|
-
const string name;
|
770
|
-
/** A secret token that may be known among all processes in this Group. Used for securing
|
771
|
-
* intra-group process communication.
|
772
|
-
*/
|
773
|
-
char secret[SECRET_SIZE];
|
774
286
|
/** A UUID that's generated on Group initialization, and changes every time
|
775
287
|
* the Group receives a restart command. Allows Union Station to track app
|
776
288
|
* restarts. This information is public.
|
777
289
|
*/
|
778
290
|
string uuid;
|
779
|
-
ComponentInfo componentInfo;
|
780
291
|
|
781
292
|
/**
|
782
293
|
* Processes are categorized as enabled, disabling or disabled.
|
@@ -899,668 +410,82 @@ public:
|
|
899
410
|
* Invariant:
|
900
411
|
* (lifeStatus == ALIVE) == (spawner != NULL)
|
901
412
|
*/
|
902
|
-
SpawnerPtr spawner;
|
413
|
+
SpawningKit::SpawnerPtr spawner;
|
903
414
|
|
904
415
|
|
905
|
-
|
906
|
-
* Constructors and destructors
|
907
|
-
********************************************/
|
416
|
+
/****** Initialization and shutdown ******/
|
908
417
|
|
909
|
-
Group(
|
418
|
+
Group(Pool *pool, const Options &options);
|
910
419
|
~Group();
|
911
|
-
|
912
|
-
|
913
|
-
/**
|
914
|
-
* Must be called before destroying a Group. You can optionally provide a
|
915
|
-
* callback so that you are notified when shutdown has finished.
|
916
|
-
*
|
917
|
-
* The caller is responsible for migrating waiters on the getWaitlist.
|
918
|
-
*
|
919
|
-
* One of the post lock actions can potentially perform a long-running
|
920
|
-
* operation, so running them in a thread is advised.
|
921
|
-
*/
|
420
|
+
bool initialize();
|
922
421
|
void shutdown(const Callback &callback,
|
923
|
-
boost::container::vector<Callback> &postLockActions)
|
924
|
-
{
|
925
|
-
assert(isAlive());
|
926
|
-
|
927
|
-
P_DEBUG("Begin shutting down group " << name);
|
928
|
-
shutdownCallback = callback;
|
929
|
-
detachAll(postLockActions);
|
930
|
-
startCheckingDetachedProcesses(true);
|
931
|
-
interruptableThreads.interrupt_all();
|
932
|
-
postLockActions.push_back(boost::bind(doCleanupSpawner, spawner));
|
933
|
-
spawner.reset();
|
934
|
-
selfPointer = shared_from_this();
|
935
|
-
assert(disableWaitlist.empty());
|
936
|
-
lifeStatus.store(SHUTTING_DOWN, boost::memory_order_release);
|
937
|
-
}
|
938
|
-
|
939
|
-
|
940
|
-
/********************************************
|
941
|
-
* Life time and back-reference methods
|
942
|
-
********************************************/
|
422
|
+
boost::container::vector<Callback> &postLockActions);
|
943
423
|
|
944
|
-
|
945
|
-
* Thread-safe.
|
946
|
-
* @pre getLifeState() != SHUT_DOWN
|
947
|
-
* @post result != NULL
|
948
|
-
*/
|
949
|
-
SuperGroup *getSuperGroup() const {
|
950
|
-
return superGroup;
|
951
|
-
}
|
952
|
-
|
953
|
-
void setSuperGroup(SuperGroup *superGroup) {
|
954
|
-
assert(this->superGroup == NULL);
|
955
|
-
this->superGroup = superGroup;
|
956
|
-
}
|
957
|
-
|
958
|
-
/**
|
959
|
-
* Thread-safe.
|
960
|
-
* @pre getLifeState() != SHUT_DOWN
|
961
|
-
* @post result != NULL
|
962
|
-
*/
|
963
|
-
OXT_FORCE_INLINE Pool *getPool() const;
|
424
|
+
/****** Life time, basic info, backreferences and related objects ******/
|
964
425
|
|
965
|
-
|
966
|
-
|
967
|
-
return getLifeStatus() == ALIVE;
|
968
|
-
}
|
426
|
+
bool isAlive() const;
|
427
|
+
OXT_FORCE_INLINE LifeStatus getLifeStatus() const;
|
969
428
|
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
return (LifeStatus) lifeStatus.load(boost::memory_order_acquire);
|
974
|
-
}
|
429
|
+
StaticString getName() const;
|
430
|
+
const BasicGroupInfo &getInfo();
|
431
|
+
const ApiKey &getApiKey() const;
|
975
432
|
|
433
|
+
OXT_FORCE_INLINE Pool *getPool() const;
|
434
|
+
Context *getContext() const;
|
435
|
+
psg_pool_t *getPallocPool() const;
|
436
|
+
const ResourceLocator &getResourceLocator() const;
|
976
437
|
|
977
|
-
|
978
|
-
* Core methods
|
979
|
-
********************************************/
|
438
|
+
/****** Session management ******/
|
980
439
|
|
981
440
|
SessionPtr get(const Options &newOptions, const GetCallback &callback,
|
982
|
-
boost::container::vector<Callback> &postLockActions)
|
983
|
-
{
|
984
|
-
assert(isAlive());
|
985
|
-
|
986
|
-
if (OXT_LIKELY(!restarting())) {
|
987
|
-
if (OXT_UNLIKELY(needsRestart(newOptions))) {
|
988
|
-
restart(newOptions);
|
989
|
-
} else {
|
990
|
-
mergeOptions(newOptions);
|
991
|
-
}
|
992
|
-
if (OXT_UNLIKELY(!newOptions.noop && shouldSpawnForGetAction())) {
|
993
|
-
// If we're trying to spawn the first process for this group, and
|
994
|
-
// spawning failed because the pool is at full capacity, then we
|
995
|
-
// try to kill some random idle process in the pool and try again.
|
996
|
-
if (spawn() == SR_ERR_POOL_AT_FULL_CAPACITY && enabledCount == 0) {
|
997
|
-
P_INFO("Unable to spawn the the sole process for group " << name <<
|
998
|
-
" because the max pool size has been reached. Trying " <<
|
999
|
-
"to shutdown another idle process to free capacity...");
|
1000
|
-
if (poolForceFreeCapacity(this, postLockActions) != NULL) {
|
1001
|
-
SpawnResult result = spawn();
|
1002
|
-
assert(result == SR_OK);
|
1003
|
-
(void) result;
|
1004
|
-
} else {
|
1005
|
-
P_INFO("There are no processes right now that are eligible "
|
1006
|
-
"for shutdown. Will try again later.");
|
1007
|
-
}
|
1008
|
-
}
|
1009
|
-
}
|
1010
|
-
}
|
1011
|
-
|
1012
|
-
if (OXT_UNLIKELY(newOptions.noop)) {
|
1013
|
-
return nullProcess->createSessionObject((Socket *) NULL);
|
1014
|
-
}
|
1015
|
-
|
1016
|
-
if (OXT_UNLIKELY(enabledCount == 0)) {
|
1017
|
-
/* We don't have any processes yet, but they're on the way.
|
1018
|
-
*
|
1019
|
-
* We have some choices here. If there are disabling processes
|
1020
|
-
* then we generally want to use them, except:
|
1021
|
-
* - When non-rolling restarting because those disabling processes
|
1022
|
-
* are from the old version.
|
1023
|
-
* - When all disabling processes are totally busy.
|
1024
|
-
*
|
1025
|
-
* Whenever a disabling process cannot be used, call the callback
|
1026
|
-
* after a process has been spawned or has failed to spawn, or
|
1027
|
-
* when a disabling process becomes available.
|
1028
|
-
*/
|
1029
|
-
assert(m_spawning || restarting() || poolAtFullCapacity());
|
1030
|
-
|
1031
|
-
if (disablingCount > 0 && !restarting()) {
|
1032
|
-
Process *process = findProcessWithLowestBusyness(disablingProcesses);
|
1033
|
-
assert(process != NULL);
|
1034
|
-
if (!process->isTotallyBusy()) {
|
1035
|
-
return newSession(process, newOptions.currentTime);
|
1036
|
-
}
|
1037
|
-
}
|
1038
|
-
|
1039
|
-
if (pushGetWaiter(newOptions, callback, postLockActions)) {
|
1040
|
-
P_DEBUG("No session checked out yet: group is spawning or restarting");
|
1041
|
-
}
|
1042
|
-
return SessionPtr();
|
1043
|
-
} else {
|
1044
|
-
RouteResult result = route(newOptions);
|
1045
|
-
if (result.process == NULL) {
|
1046
|
-
/* Looks like all processes are totally busy.
|
1047
|
-
* Wait until a new one has been spawned or until
|
1048
|
-
* resources have become free.
|
1049
|
-
*/
|
1050
|
-
if (pushGetWaiter(newOptions, callback, postLockActions)) {
|
1051
|
-
P_DEBUG("No session checked out yet: all processes are at full capacity");
|
1052
|
-
}
|
1053
|
-
return SessionPtr();
|
1054
|
-
} else {
|
1055
|
-
P_DEBUG("Session checked out from process " << result.process->inspect());
|
1056
|
-
return newSession(result.process, newOptions.currentTime);
|
1057
|
-
}
|
1058
|
-
}
|
1059
|
-
}
|
1060
|
-
|
1061
|
-
|
1062
|
-
/********************************************
|
1063
|
-
* State mutation methods
|
1064
|
-
********************************************/
|
1065
|
-
|
1066
|
-
// Thread-safe, but only call outside the pool lock!
|
1067
|
-
void requestOOBW(const ProcessPtr &process);
|
1068
|
-
|
1069
|
-
/**
|
1070
|
-
* Attaches the given process to this Group and mark it as enabled. This
|
1071
|
-
* function doesn't touch `getWaitlist` so be sure to fix its invariants
|
1072
|
-
* afterwards if necessary, e.g. by calling `assignSessionsToGetWaiters()`.
|
1073
|
-
*/
|
1074
|
-
AttachResult attach(const SpawnObject &spawnObject,
|
1075
|
-
boost::container::vector<Callback> &postLockActions)
|
1076
|
-
{
|
1077
|
-
TRACE_POINT();
|
1078
|
-
const ProcessPtr &process = spawnObject.process;
|
1079
|
-
assert(process->getGroup() == NULL || process->getGroup() == this);
|
1080
|
-
assert(process->isAlive());
|
1081
|
-
assert(isAlive());
|
1082
|
-
|
1083
|
-
if (processUpperLimitsReached()) {
|
1084
|
-
return AR_GROUP_UPPER_LIMITS_REACHED;
|
1085
|
-
} else if (poolAtFullCapacity()) {
|
1086
|
-
return AR_POOL_AT_FULL_CAPACITY;
|
1087
|
-
} else if (!isWaitingForCapacity() && anotherGroupIsWaitingForCapacity()) {
|
1088
|
-
return AR_ANOTHER_GROUP_IS_WAITING_FOR_CAPACITY;
|
1089
|
-
}
|
1090
|
-
|
1091
|
-
process->setGroup(this);
|
1092
|
-
process->stickySessionId = generateStickySessionId();
|
1093
|
-
P_DEBUG("Attaching process " << process->inspect());
|
1094
|
-
addProcessToList(process, enabledProcesses);
|
1095
|
-
|
1096
|
-
if (spawnObject.pool != getPallocPool()) {
|
1097
|
-
process->recreateStrings(getPallocPool());
|
1098
|
-
}
|
1099
|
-
|
1100
|
-
/* Now that there are enough resources, relevant processes in
|
1101
|
-
* 'disableWaitlist' can be disabled.
|
1102
|
-
*/
|
1103
|
-
deque<DisableWaiter>::const_iterator it, end = disableWaitlist.end();
|
1104
|
-
deque<DisableWaiter> newDisableWaitlist;
|
1105
|
-
for (it = disableWaitlist.begin(); it != end; it++) {
|
1106
|
-
const DisableWaiter &waiter = *it;
|
1107
|
-
const ProcessPtr process2 = waiter.process;
|
1108
|
-
// The same process can appear multiple times in disableWaitlist.
|
1109
|
-
assert(process2->enabled == Process::DISABLING
|
1110
|
-
|| process2->enabled == Process::DISABLED);
|
1111
|
-
if (process2->sessions == 0) {
|
1112
|
-
if (process2->enabled == Process::DISABLING) {
|
1113
|
-
P_DEBUG("Disabling DISABLING process " << process2->inspect() <<
|
1114
|
-
"; disable command succeeded immediately");
|
1115
|
-
removeProcessFromList(process2, disablingProcesses);
|
1116
|
-
addProcessToList(process2, disabledProcesses);
|
1117
|
-
} else {
|
1118
|
-
P_DEBUG("Disabling (already disabled) DISABLING process " <<
|
1119
|
-
process2->inspect() << "; disable command succeeded immediately");
|
1120
|
-
}
|
1121
|
-
postLockActions.push_back(boost::bind(waiter.callback, process2, DR_SUCCESS));
|
1122
|
-
} else {
|
1123
|
-
newDisableWaitlist.push_back(waiter);
|
1124
|
-
}
|
1125
|
-
}
|
1126
|
-
disableWaitlist = newDisableWaitlist;
|
1127
|
-
|
1128
|
-
// Update GC sleep timer.
|
1129
|
-
wakeUpGarbageCollector();
|
1130
|
-
|
1131
|
-
postLockActions.push_back(boost::bind(&Group::runAttachHooks, this, process));
|
1132
|
-
|
1133
|
-
return AR_OK;
|
1134
|
-
}
|
441
|
+
boost::container::vector<Callback> &postLockActions);
|
1135
442
|
|
1136
|
-
|
1137
|
-
* Detaches the given process from this Group. This function doesn't touch
|
1138
|
-
* getWaitlist so be sure to fix its invariants afterwards if necessary.
|
1139
|
-
* `pool->detachProcessUnlocked()` does that so you should usually use
|
1140
|
-
* that method over this one.
|
1141
|
-
*/
|
1142
|
-
void detach(const ProcessPtr &process, boost::container::vector<Callback> &postLockActions) {
|
1143
|
-
TRACE_POINT();
|
1144
|
-
assert(process->getGroup() == this);
|
1145
|
-
assert(process->isAlive());
|
1146
|
-
assert(isAlive());
|
1147
|
-
|
1148
|
-
if (process->enabled == Process::DETACHED) {
|
1149
|
-
P_DEBUG("Detaching process " << process->inspect() << ", which was already being detached");
|
1150
|
-
return;
|
1151
|
-
}
|
1152
|
-
|
1153
|
-
const ProcessPtr p = process; // Keep an extra reference just in case.
|
1154
|
-
P_DEBUG("Detaching process " << process->inspect());
|
1155
|
-
|
1156
|
-
if (process->enabled == Process::ENABLED || process->enabled == Process::DISABLING) {
|
1157
|
-
assert(enabledCount > 0 || disablingCount > 0);
|
1158
|
-
if (process->enabled == Process::ENABLED) {
|
1159
|
-
removeProcessFromList(process, enabledProcesses);
|
1160
|
-
} else {
|
1161
|
-
removeProcessFromList(process, disablingProcesses);
|
1162
|
-
removeFromDisableWaitlist(process, DR_NOOP, postLockActions);
|
1163
|
-
}
|
1164
|
-
} else {
|
1165
|
-
assert(process->enabled == Process::DISABLED);
|
1166
|
-
assert(!disabledProcesses.empty());
|
1167
|
-
removeProcessFromList(process, disabledProcesses);
|
1168
|
-
}
|
1169
|
-
|
1170
|
-
addProcessToList(process, detachedProcesses);
|
1171
|
-
startCheckingDetachedProcesses(false);
|
1172
|
-
|
1173
|
-
postLockActions.push_back(boost::bind(&Group::runDetachHooks, this, process));
|
1174
|
-
}
|
443
|
+
/****** Spawning and restarting ******/
|
1175
444
|
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
*/
|
1180
|
-
void detachAll(boost::container::vector<Callback> &postLockActions) {
|
1181
|
-
assert(isAlive());
|
1182
|
-
P_DEBUG("Detaching all processes in group " << name);
|
1183
|
-
|
1184
|
-
foreach (ProcessPtr process, enabledProcesses) {
|
1185
|
-
addProcessToList(process, detachedProcesses);
|
1186
|
-
}
|
1187
|
-
foreach (ProcessPtr process, disablingProcesses) {
|
1188
|
-
addProcessToList(process, detachedProcesses);
|
1189
|
-
}
|
1190
|
-
foreach (ProcessPtr process, disabledProcesses) {
|
1191
|
-
addProcessToList(process, detachedProcesses);
|
1192
|
-
}
|
1193
|
-
|
1194
|
-
enabledProcesses.clear();
|
1195
|
-
disablingProcesses.clear();
|
1196
|
-
disabledProcesses.clear();
|
1197
|
-
enabledProcessBusynessLevels.clear();
|
1198
|
-
enabledCount = 0;
|
1199
|
-
disablingCount = 0;
|
1200
|
-
disabledCount = 0;
|
1201
|
-
nEnabledProcessesTotallyBusy = 0;
|
1202
|
-
clearDisableWaitlist(DR_NOOP, postLockActions);
|
1203
|
-
startCheckingDetachedProcesses(false);
|
1204
|
-
}
|
445
|
+
void restart(const Options &options, RestartMethod method = RM_DEFAULT);
|
446
|
+
bool restarting() const;
|
447
|
+
bool needsRestart(const Options &options);
|
1205
448
|
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
assert(process->getGroup() == this);
|
1212
|
-
assert(process->isAlive());
|
1213
|
-
assert(isAlive());
|
1214
|
-
|
1215
|
-
if (process->enabled == Process::DISABLING) {
|
1216
|
-
P_DEBUG("Enabling DISABLING process " << process->inspect());
|
1217
|
-
removeProcessFromList(process, disablingProcesses);
|
1218
|
-
addProcessToList(process, enabledProcesses);
|
1219
|
-
removeFromDisableWaitlist(process, DR_CANCELED, postLockActions);
|
1220
|
-
} else if (process->enabled == Process::DISABLED) {
|
1221
|
-
P_DEBUG("Enabling DISABLED process " << process->inspect());
|
1222
|
-
removeProcessFromList(process, disabledProcesses);
|
1223
|
-
addProcessToList(process, enabledProcesses);
|
1224
|
-
} else {
|
1225
|
-
P_DEBUG("Enabling ENABLED process " << process->inspect());
|
1226
|
-
}
|
1227
|
-
}
|
449
|
+
SpawnResult spawn();
|
450
|
+
bool spawning() const;
|
451
|
+
bool shouldSpawn() const;
|
452
|
+
bool shouldSpawnForGetAction() const;
|
453
|
+
bool allowSpawn() const;
|
1228
454
|
|
1229
|
-
|
1230
|
-
* Marks the given process as disabled. Returns DR_SUCCESS, DR_DEFERRED
|
1231
|
-
* or DR_NOOP. If the result is DR_DEFERRED, then the callback will be
|
1232
|
-
* called later with the result of this action.
|
1233
|
-
*/
|
1234
|
-
DisableResult disable(const ProcessPtr &process, const DisableCallback &callback) {
|
1235
|
-
assert(process->getGroup() == this);
|
1236
|
-
assert(process->isAlive());
|
1237
|
-
assert(isAlive());
|
1238
|
-
|
1239
|
-
if (process->enabled == Process::ENABLED) {
|
1240
|
-
P_DEBUG("Disabling ENABLED process " << process->inspect() <<
|
1241
|
-
"; enabledCount=" << enabledCount << ", process.sessions=" << process->sessions);
|
1242
|
-
assert(enabledCount >= 0);
|
1243
|
-
if (enabledCount == 1 && !allowSpawn()) {
|
1244
|
-
P_WARN("Cannot disable sole enabled process in group " << name <<
|
1245
|
-
" because spawning is not allowed according to the current" <<
|
1246
|
-
" configuration options");
|
1247
|
-
return DR_ERROR;
|
1248
|
-
} else if (enabledCount <= 1 || process->sessions > 0) {
|
1249
|
-
removeProcessFromList(process, enabledProcesses);
|
1250
|
-
addProcessToList(process, disablingProcesses);
|
1251
|
-
disableWaitlist.push_back(DisableWaiter(process, callback));
|
1252
|
-
if (enabledCount == 0) {
|
1253
|
-
/* All processes are going to be disabled, so in order
|
1254
|
-
* to avoid blocking requests we first spawn a new process
|
1255
|
-
* and disable this process after the other one is done
|
1256
|
-
* spawning. We do this irregardless of resource limits
|
1257
|
-
* because this is an exceptional situation.
|
1258
|
-
*/
|
1259
|
-
P_DEBUG("Spawning a new process to avoid the disable action from blocking requests");
|
1260
|
-
spawn();
|
1261
|
-
}
|
1262
|
-
P_DEBUG("Deferring disable command completion");
|
1263
|
-
return DR_DEFERRED;
|
1264
|
-
} else {
|
1265
|
-
removeProcessFromList(process, enabledProcesses);
|
1266
|
-
addProcessToList(process, disabledProcesses);
|
1267
|
-
P_DEBUG("Disable command succeeded immediately");
|
1268
|
-
return DR_SUCCESS;
|
1269
|
-
}
|
1270
|
-
} else if (process->enabled == Process::DISABLING) {
|
1271
|
-
assert(disablingCount > 0);
|
1272
|
-
disableWaitlist.push_back(DisableWaiter(process, callback));
|
1273
|
-
P_DEBUG("Disabling DISABLING process " << process->inspect() <<
|
1274
|
-
name << "; command queued, deferring disable command completion");
|
1275
|
-
return DR_DEFERRED;
|
1276
|
-
} else {
|
1277
|
-
assert(disabledCount > 0);
|
1278
|
-
P_DEBUG("Disabling DISABLED process " << process->inspect() <<
|
1279
|
-
name << "; disable command succeeded immediately");
|
1280
|
-
return DR_NOOP;
|
1281
|
-
}
|
1282
|
-
}
|
455
|
+
/****** Process list management ******/
|
1283
456
|
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
*/
|
1290
|
-
SpawnResult spawn() {
|
1291
|
-
assert(isAlive());
|
1292
|
-
if (m_spawning) {
|
1293
|
-
return SR_IN_PROGRESS;
|
1294
|
-
} else if (restarting()) {
|
1295
|
-
return SR_ERR_RESTARTING;
|
1296
|
-
} else if (processUpperLimitsReached()) {
|
1297
|
-
return SR_ERR_GROUP_UPPER_LIMITS_REACHED;
|
1298
|
-
} else if (poolAtFullCapacity()) {
|
1299
|
-
return SR_ERR_POOL_AT_FULL_CAPACITY;
|
1300
|
-
} else {
|
1301
|
-
P_DEBUG("Requested spawning of new process for group " << name);
|
1302
|
-
interruptableThreads.create_thread(
|
1303
|
-
boost::bind(&Group::spawnThreadMain,
|
1304
|
-
this, shared_from_this(), spawner,
|
1305
|
-
options.copyAndPersist().clearPerRequestFields(),
|
1306
|
-
restartsInitiated),
|
1307
|
-
"Group process spawner: " + name,
|
1308
|
-
POOL_HELPER_THREAD_STACK_SIZE);
|
1309
|
-
m_spawning = true;
|
1310
|
-
processesBeingSpawned++;
|
1311
|
-
return SR_OK;
|
1312
|
-
}
|
1313
|
-
}
|
1314
|
-
|
1315
|
-
void cleanupSpawner(boost::container::vector<Callback> &postLockActions) {
|
1316
|
-
assert(isAlive());
|
1317
|
-
postLockActions.push_back(boost::bind(doCleanupSpawner, spawner));
|
1318
|
-
}
|
457
|
+
AttachResult attach(const ProcessPtr &process,
|
458
|
+
boost::container::vector<Callback> &postLockActions);
|
459
|
+
void detach(const ProcessPtr &process,
|
460
|
+
boost::container::vector<Callback> &postLockActions);
|
461
|
+
void detachAll(boost::container::vector<Callback> &postLockActions);
|
1319
462
|
|
1320
|
-
void
|
463
|
+
void enable(const ProcessPtr &process,
|
464
|
+
boost::container::vector<Callback> &postLockActions);
|
465
|
+
DisableResult disable(const ProcessPtr &process, const DisableCallback &callback);
|
1321
466
|
|
467
|
+
/****** State inspection ******/
|
1322
468
|
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
469
|
+
unsigned int getProcessCount() const;
|
470
|
+
bool processLowerLimitsSatisfied() const;
|
471
|
+
bool processUpperLimitsReached() const;
|
472
|
+
bool allEnabledProcessesAreTotallyBusy() const;
|
1326
473
|
|
1327
|
-
unsigned int
|
1328
|
-
|
1329
|
-
|
474
|
+
unsigned int capacityUsed() const;
|
475
|
+
bool isWaitingForCapacity() const;
|
476
|
+
bool garbageCollectable(unsigned long long now = 0) const;
|
1330
477
|
|
1331
|
-
|
1332
|
-
* Returns the number of processes in this group that should be part of the
|
1333
|
-
* ApplicationPool process limits calculations.
|
1334
|
-
*/
|
1335
|
-
unsigned int capacityUsed() const {
|
1336
|
-
return enabledCount + disablingCount + disabledCount + processesBeingSpawned;
|
1337
|
-
}
|
478
|
+
void inspectXml(std::ostream &stream, bool includeSecrets = true) const;
|
1338
479
|
|
1339
|
-
|
1340
|
-
* Returns whether the lower bound of the group-specific process limits
|
1341
|
-
* have been satisfied. Note that even if the result is false, the pool limits
|
1342
|
-
* may not allow spawning, so you should check `pool->atFullCapacity()` too.
|
1343
|
-
*/
|
1344
|
-
bool processLowerLimitsSatisfied() const {
|
1345
|
-
return capacityUsed() >= options.minProcesses;
|
1346
|
-
}
|
480
|
+
/****** Out-of-band work ******/
|
1347
481
|
|
1348
|
-
|
1349
|
-
* Returns whether the upper bound of the group-specific process limits have
|
1350
|
-
* been reached, or surpassed. Does not check whether pool limits have been
|
1351
|
-
* reached. Use `pool->atFullCapacity()` to check for that.
|
1352
|
-
*/
|
1353
|
-
bool processUpperLimitsReached() const {
|
1354
|
-
return options.maxProcesses != 0 && capacityUsed() >= options.maxProcesses;
|
1355
|
-
}
|
482
|
+
void requestOOBW(const ProcessPtr &process);
|
1356
483
|
|
1357
|
-
|
1358
|
-
* Returns whether all enabled processes are totally busy. If so, another
|
1359
|
-
* process should be spawned, if allowed by the process limits.
|
1360
|
-
* Returns false if there are no enabled processes.
|
1361
|
-
*/
|
1362
|
-
bool allEnabledProcessesAreTotallyBusy() const {
|
1363
|
-
return nEnabledProcessesTotallyBusy == enabledCount;
|
1364
|
-
}
|
484
|
+
/****** Miscellaneous ******/
|
1365
485
|
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
*/
|
1370
|
-
bool isWaitingForCapacity() const {
|
1371
|
-
return enabledProcesses.empty()
|
1372
|
-
&& processesBeingSpawned == 0
|
1373
|
-
&& !m_restarting
|
1374
|
-
&& !getWaitlist.empty();
|
1375
|
-
}
|
1376
|
-
|
1377
|
-
bool garbageCollectable(unsigned long long now = 0) const {
|
1378
|
-
/* if (now == 0) {
|
1379
|
-
now = SystemTime::getUsec();
|
1380
|
-
}
|
1381
|
-
return busyness() == 0
|
1382
|
-
&& getWaitlist.empty()
|
1383
|
-
&& disabledProcesses.empty()
|
1384
|
-
&& options.getMaxPreloaderIdleTime() != 0
|
1385
|
-
&& now - spawner->lastUsed() >
|
1386
|
-
(unsigned long long) options.getMaxPreloaderIdleTime() * 1000000; */
|
1387
|
-
return false;
|
1388
|
-
}
|
1389
|
-
|
1390
|
-
/** Whether a new process should be spawned for this group. */
|
1391
|
-
bool shouldSpawn() const;
|
1392
|
-
/** Whether a new process should be spawned for this group in the
|
1393
|
-
* specific case that another get action is to be performed.
|
1394
|
-
*/
|
1395
|
-
bool shouldSpawnForGetAction() const;
|
1396
|
-
|
1397
|
-
/**
|
1398
|
-
* Whether a new process is allowed to be spawned for this group,
|
1399
|
-
* i.e. whether the upper processes limits have not been reached.
|
1400
|
-
*/
|
1401
|
-
bool allowSpawn() const {
|
1402
|
-
return isAlive()
|
1403
|
-
&& !processUpperLimitsReached()
|
1404
|
-
&& !poolAtFullCapacity();
|
1405
|
-
}
|
1406
|
-
|
1407
|
-
bool needsRestart(const Options &options) {
|
1408
|
-
if (m_restarting) {
|
1409
|
-
return false;
|
1410
|
-
} else {
|
1411
|
-
time_t now;
|
1412
|
-
struct stat buf;
|
1413
|
-
|
1414
|
-
if (options.currentTime != 0) {
|
1415
|
-
now = options.currentTime / 1000000;
|
1416
|
-
} else {
|
1417
|
-
now = SystemTime::get();
|
1418
|
-
}
|
1419
|
-
|
1420
|
-
if (lastRestartFileCheckTime == 0) {
|
1421
|
-
// First time we call needsRestart() for this group.
|
1422
|
-
if (syscalls::stat(restartFile.c_str(), &buf) == 0) {
|
1423
|
-
lastRestartFileMtime = buf.st_mtime;
|
1424
|
-
} else {
|
1425
|
-
lastRestartFileMtime = 0;
|
1426
|
-
}
|
1427
|
-
lastRestartFileCheckTime = now;
|
1428
|
-
return false;
|
1429
|
-
|
1430
|
-
} else if (lastRestartFileCheckTime <= now - (time_t) options.statThrottleRate) {
|
1431
|
-
// Not first time we call needsRestart() for this group.
|
1432
|
-
// Stat throttle time has passed.
|
1433
|
-
bool restart;
|
1434
|
-
|
1435
|
-
lastRestartFileCheckTime = now;
|
1436
|
-
|
1437
|
-
if (lastRestartFileMtime > 0) {
|
1438
|
-
// restart.txt existed before
|
1439
|
-
if (syscalls::stat(restartFile.c_str(), &buf) == -1) {
|
1440
|
-
// restart.txt no longer exists
|
1441
|
-
lastRestartFileMtime = buf.st_mtime;
|
1442
|
-
restart = false;
|
1443
|
-
} else if (buf.st_mtime != lastRestartFileMtime) {
|
1444
|
-
// restart.txt's mtime has changed
|
1445
|
-
lastRestartFileMtime = buf.st_mtime;
|
1446
|
-
restart = true;
|
1447
|
-
} else {
|
1448
|
-
restart = false;
|
1449
|
-
}
|
1450
|
-
} else {
|
1451
|
-
// restart.txt didn't exist before
|
1452
|
-
if (syscalls::stat(restartFile.c_str(), &buf) == 0) {
|
1453
|
-
// restart.txt now exists
|
1454
|
-
lastRestartFileMtime = buf.st_mtime;
|
1455
|
-
restart = true;
|
1456
|
-
} else {
|
1457
|
-
// restart.txt still doesn't exist
|
1458
|
-
lastRestartFileMtime = 0;
|
1459
|
-
restart = false;
|
1460
|
-
}
|
1461
|
-
}
|
1462
|
-
|
1463
|
-
if (!restart) {
|
1464
|
-
alwaysRestartFileExists = restart =
|
1465
|
-
syscalls::stat(alwaysRestartFile.c_str(), &buf) == 0;
|
1466
|
-
}
|
1467
|
-
|
1468
|
-
return restart;
|
1469
|
-
|
1470
|
-
} else {
|
1471
|
-
// Not first time we call needsRestart() for this group.
|
1472
|
-
// Still within stat throttling window.
|
1473
|
-
if (alwaysRestartFileExists) {
|
1474
|
-
// always_restart.txt existed before
|
1475
|
-
alwaysRestartFileExists = syscalls::stat(
|
1476
|
-
alwaysRestartFile.c_str(), &buf) == 0;
|
1477
|
-
return alwaysRestartFileExists;
|
1478
|
-
} else {
|
1479
|
-
// Don't check until stat throttling window is over
|
1480
|
-
return false;
|
1481
|
-
}
|
1482
|
-
}
|
1483
|
-
}
|
1484
|
-
}
|
1485
|
-
|
1486
|
-
bool spawning() const {
|
1487
|
-
return m_spawning;
|
1488
|
-
}
|
1489
|
-
|
1490
|
-
bool restarting() const {
|
1491
|
-
return m_restarting;
|
1492
|
-
}
|
1493
|
-
|
1494
|
-
template<typename Stream>
|
1495
|
-
void inspectXml(Stream &stream, bool includeSecrets = true) const {
|
1496
|
-
ProcessList::const_iterator it;
|
1497
|
-
|
1498
|
-
stream << "<name>" << escapeForXml(name) << "</name>";
|
1499
|
-
stream << "<component_name>" << escapeForXml(componentInfo.name) << "</component_name>";
|
1500
|
-
stream << "<app_root>" << escapeForXml(options.appRoot) << "</app_root>";
|
1501
|
-
stream << "<app_type>" << escapeForXml(options.appType) << "</app_type>";
|
1502
|
-
stream << "<environment>" << escapeForXml(options.environment) << "</environment>";
|
1503
|
-
stream << "<uuid>" << toString(uuid) << "</uuid>";
|
1504
|
-
stream << "<enabled_process_count>" << enabledCount << "</enabled_process_count>";
|
1505
|
-
stream << "<disabling_process_count>" << disablingCount << "</disabling_process_count>";
|
1506
|
-
stream << "<disabled_process_count>" << disabledCount << "</disabled_process_count>";
|
1507
|
-
stream << "<capacity_used>" << capacityUsed() << "</capacity_used>";
|
1508
|
-
stream << "<get_wait_list_size>" << getWaitlist.size() << "</get_wait_list_size>";
|
1509
|
-
stream << "<disable_wait_list_size>" << disableWaitlist.size() << "</disable_wait_list_size>";
|
1510
|
-
stream << "<processes_being_spawned>" << processesBeingSpawned << "</processes_being_spawned>";
|
1511
|
-
if (m_spawning) {
|
1512
|
-
stream << "<spawning/>";
|
1513
|
-
}
|
1514
|
-
if (restarting()) {
|
1515
|
-
stream << "<restarting/>";
|
1516
|
-
}
|
1517
|
-
if (includeSecrets) {
|
1518
|
-
stream << "<secret>" << escapeForXml(StaticString(secret, SECRET_SIZE)) << "</secret>";
|
1519
|
-
}
|
1520
|
-
LifeStatus lifeStatus = (LifeStatus) this->lifeStatus.load(boost::memory_order_relaxed);
|
1521
|
-
switch (lifeStatus) {
|
1522
|
-
case ALIVE:
|
1523
|
-
stream << "<life_status>ALIVE</life_status>";
|
1524
|
-
break;
|
1525
|
-
case SHUTTING_DOWN:
|
1526
|
-
stream << "<life_status>SHUTTING_DOWN</life_status>";
|
1527
|
-
break;
|
1528
|
-
case SHUT_DOWN:
|
1529
|
-
stream << "<life_status>SHUT_DOWN</life_status>";
|
1530
|
-
break;
|
1531
|
-
default:
|
1532
|
-
P_BUG("Unknown 'lifeStatus' state " << lifeStatus);
|
1533
|
-
}
|
1534
|
-
|
1535
|
-
stream << "<options>";
|
1536
|
-
options.toXml(stream, getResourceLocator());
|
1537
|
-
stream << "</options>";
|
1538
|
-
|
1539
|
-
stream << "<processes>";
|
1540
|
-
|
1541
|
-
for (it = enabledProcesses.begin(); it != enabledProcesses.end(); it++) {
|
1542
|
-
stream << "<process>";
|
1543
|
-
(*it)->inspectXml(stream, includeSecrets);
|
1544
|
-
stream << "</process>";
|
1545
|
-
}
|
1546
|
-
for (it = disablingProcesses.begin(); it != disablingProcesses.end(); it++) {
|
1547
|
-
stream << "<process>";
|
1548
|
-
(*it)->inspectXml(stream, includeSecrets);
|
1549
|
-
stream << "</process>";
|
1550
|
-
}
|
1551
|
-
for (it = disabledProcesses.begin(); it != disabledProcesses.end(); it++) {
|
1552
|
-
stream << "<process>";
|
1553
|
-
(*it)->inspectXml(stream, includeSecrets);
|
1554
|
-
stream << "</process>";
|
1555
|
-
}
|
1556
|
-
for (it = detachedProcesses.begin(); it != detachedProcesses.end(); it++) {
|
1557
|
-
stream << "<process>";
|
1558
|
-
(*it)->inspectXml(stream, includeSecrets);
|
1559
|
-
stream << "</process>";
|
1560
|
-
}
|
1561
|
-
|
1562
|
-
stream << "</processes>";
|
1563
|
-
}
|
486
|
+
void cleanupSpawner(boost::container::vector<Callback> &postLockActions);
|
487
|
+
bool authorizeByUid(uid_t uid) const;
|
488
|
+
bool authorizeByApiKey(const ApiKey &key) const;
|
1564
489
|
};
|
1565
490
|
|
1566
491
|
|