Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track and prioritize routing according to application restart generations #2564

Merged
merged 36 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
20df215
add generation field to process
CamJN Sep 9, 2024
744c2b2
use process generation to inform routing
CamJN Sep 9, 2024
b5dd97a
missed changes
CamJN Sep 9, 2024
df6f2ac
address feedback
CamJN Sep 22, 2024
38187b2
add a test to process test suite
CamJN Sep 22, 2024
a376e8e
add another test
CamJN Sep 23, 2024
824166d
test stub for proper testing of routing algo
CamJN Sep 23, 2024
60d254b
impl new algo
CamJN Sep 27, 2024
6b739f3
update changelog
CamJN Sep 30, 2024
fd21774
Fix indentation
FooBarWidget Oct 3, 2024
c0d99a6
Remove the new test in ProcessTest.cpp because it's pointless
FooBarWidget Oct 3, 2024
f8481e1
Fix typo, set test name
FooBarWidget Oct 3, 2024
6ab1b52
Use spawnStartTime instead of spawnerCreationTime
FooBarWidget Oct 3, 2024
fc86370
Merge branch 'stable-6.0' into feature/app_generations
FooBarWidget Oct 4, 2024
ee8e35c
Merge branch 'stable-6.0' into feature/app_generations
FooBarWidget Oct 12, 2024
cec7638
remove unused headers
CamJN Oct 14, 2024
a45c981
change test condition per hongli’s request
CamJN Oct 14, 2024
8cff85c
don’t select a totally busy process
CamJN Oct 14, 2024
b40c7d2
fallback to best possible process in case all are totally busy
CamJN Oct 14, 2024
506dccd
Merge remote-tracking branch 'origin/stable-6.0' into feature/app_gen…
CamJN Oct 16, 2024
04fb08b
Merge remote-tracking branch 'origin/stable-6.0' into feature/app_gen…
CamJN Oct 17, 2024
dec5f73
fix pool test for process generation incrementing
CamJN Oct 17, 2024
6de82a7
try to make test 8 match new expectations of process selection
CamJN Oct 18, 2024
3b0605a
test doesn’t process requests on totallybusy processes obviously
CamJN Oct 21, 2024
815e8e3
Merge remote-tracking branch 'origin/stable-6.0' into feature/app_gen…
CamJN Oct 21, 2024
c76fb2d
Tests: fix detecting Python on systems with only "python3" command
FooBarWidget Oct 27, 2024
7bceab1
Fix indenting and coding style
FooBarWidget Oct 29, 2024
50bfe8d
Merge branch 'stable-6.0' into feature/app_generations
FooBarWidget Oct 29, 2024
03da686
Fix indenting
FooBarWidget Oct 29, 2024
c2b48f8
Fix up test 8, improve code docs
FooBarWidget Oct 27, 2024
d613e48
Improve tests
FooBarWidget Nov 1, 2024
8a5ebc3
address comments about only finding routeable processes
CamJN Nov 11, 2024
170c628
fixes
FooBarWidget Nov 10, 2024
aee6fbf
Merge branch 'stable-6.0' into feature/app_generations
FooBarWidget Nov 13, 2024
f06c4f7
fixup changelog
CamJN Nov 18, 2024
7a59e7b
Update changelog
FooBarWidget Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Release 6.0.24 (Not yet released)
-------------
* Update the order Passenger routes requests to app processes. Processes are now chosen based on being in the latest generation (Enterprise), then by newest process, then by oldest, then by
busyness. Closes GH-2551.
* Fix a regression from 6.0.10 where running `passenger-config system-properties` would throw an error. Closes GH-2565.
* [Enterprise] Fix a memory corruption-related crash that could occur during rolling restarting.
* [Ubuntu] Add packages for Ubuntu 24.10 "oracular".
Expand Down
6 changes: 3 additions & 3 deletions src/agent/Core/ApplicationPool/Group.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ class Group: public boost::enable_shared_from_this<Group> {
/****** Process list management ******/

Process *findProcessWithStickySessionId(unsigned int id) const;
Process *findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const;
Process *findProcessWithLowestBusyness(const ProcessList &processes) const;
Process *findEnabledProcessWithLowestBusyness() const;
Process *findBestProcessPreferringStickySessionId(unsigned int id) const;
Process *findBestProcess(const ProcessList &processes) const;
Process *findBestEnabledProcess() const;

void addProcessToList(const ProcessPtr &process, ProcessList &destination);
void removeProcessFromList(const ProcessPtr &process, ProcessList &source);
Expand Down
4 changes: 2 additions & 2 deletions src/agent/Core/ApplicationPool/Group/InternalUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ Group::createNullProcessObject() {
LockGuard l(context->memoryManagementSyncher);
Process *process = context->processObjectPool.malloc();
Guard guard(context, process);
process = new (process) Process(&info, args);
process = new (process) Process(&info, info.group->restartsInitiated, args);
process->shutdownNotRequired();
guard.clear();
return ProcessPtr(process, false);
Expand Down Expand Up @@ -221,7 +221,7 @@ Group::createProcessObject(const SpawningKit::Spawner &spawner,
LockGuard l(context->memoryManagementSyncher);
Process *process = context->processObjectPool.malloc();
Guard guard(context, process);
process = new (process) Process(&info, spawnResult, args);
process = new (process) Process(&info, info.group->restartsInitiated, spawnResult, args);
guard.clear();
return ProcessPtr(process, false);
}
Expand Down
127 changes: 91 additions & 36 deletions src/agent/Core/ApplicationPool/Group/ProcessListManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,71 +64,126 @@ Group::findProcessWithStickySessionId(unsigned int id) const {
}

Process *
Group::findProcessWithStickySessionIdOrLowestBusyness(unsigned int id) const {
int leastBusyProcessIndex = -1;
int lowestBusyness = 0;
unsigned int i, size = enabledProcessBusynessLevels.size();
const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];

for (i = 0; i < size; i++) {
Process *process = enabledProcesses[i].get();
Group::findBestProcessPreferringStickySessionId(unsigned int id) const {
Process *bestProcess = nullptr;
Process *fallbackProcess = nullptr;
ProcessList::const_iterator it;
ProcessList::const_iterator end = enabledProcesses.end();
for (it = enabledProcesses.begin(); it != end; it++) {
Process *process = (*it).get();
if (process->getStickySessionId() == id) {
return process;
} else if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
leastBusyProcessIndex = i;
lowestBusyness = enabledProcessBusynessLevels[i];
} else if (process->isTotallyBusy() && bestProcess == nullptr) {
if (fallbackProcess == nullptr ||
process->generation > fallbackProcess->generation ||
(process->generation == fallbackProcess->generation && process->spawnStartTime < fallbackProcess->spawnStartTime) ||
(process->generation == fallbackProcess->generation && process->spawnStartTime == fallbackProcess->spawnStartTime && process->busyness() < fallbackProcess->busyness())
) {
fallbackProcess = process;
}
} else if (!process->isTotallyBusy() && (bestProcess == nullptr ||
process->generation > bestProcess->generation ||
(process->generation == bestProcess->generation && process->spawnStartTime < bestProcess->spawnStartTime) ||
(process->generation == bestProcess->generation && process->spawnStartTime == bestProcess->spawnStartTime && process->busyness() < bestProcess->busyness())
)) {
bestProcess = process;
}
}

if (leastBusyProcessIndex == -1) {
return NULL;
} else {
return enabledProcesses[leastBusyProcessIndex].get();
if (bestProcess == nullptr) {
return fallbackProcess;
}
return bestProcess;
}

Process *
Group::findProcessWithLowestBusyness(const ProcessList &processes) const {
Group::findBestProcess(const ProcessList &processes) const {
if (processes.empty()) {
return NULL;
return nullptr;
}

int lowestBusyness = -1;
Process *leastBusyProcess = NULL;
Process *bestProcess = nullptr;
Process *fallbackProcess = nullptr;
ProcessList::const_iterator it;
ProcessList::const_iterator end = processes.end();
for (it = processes.begin(); it != end; it++) {
Process *process = (*it).get();
int busyness = process->busyness();
if (lowestBusyness == -1 || lowestBusyness > busyness) {
lowestBusyness = busyness;
leastBusyProcess = process;

if (process->isTotallyBusy() && bestProcess == nullptr) {
if (fallbackProcess == nullptr ||
process->generation > fallbackProcess->generation ||
(process->generation == fallbackProcess->generation && process->spawnStartTime < fallbackProcess->spawnStartTime) ||
(process->generation == fallbackProcess->generation && process->spawnStartTime == fallbackProcess->spawnStartTime && process->busyness() < fallbackProcess->busyness())
) {
fallbackProcess = process;
}
} else if (!process->isTotallyBusy() && (bestProcess == nullptr ||
process->generation > bestProcess->generation ||
(process->generation == bestProcess->generation && process->spawnStartTime < bestProcess->spawnStartTime) ||
(process->generation == bestProcess->generation && process->spawnStartTime == bestProcess->spawnStartTime && process->busyness() < bestProcess->busyness())
)) {
bestProcess = process;
}
}
return leastBusyProcess;
if (bestProcess == nullptr) {
return fallbackProcess;
}
return bestProcess;
}

/**
* Cache-optimized version of findProcessWithLowestBusyness() for the common case.
* Cache-optimized version of findBestProcess() for the common case.
*/
Process *
Group::findEnabledProcessWithLowestBusyness() const {
Group::findBestEnabledProcess() const {
CamJN marked this conversation as resolved.
Show resolved Hide resolved
CamJN marked this conversation as resolved.
Show resolved Hide resolved
if (enabledProcesses.empty()) {
return NULL;
return nullptr;
}

int leastBusyProcessIndex = -1;
int lowestBusyness = 0;
unsigned int i, size = enabledProcessBusynessLevels.size();
Process *bestProcess = nullptr;
unsigned long long bestProcessStartTime = 0;
unsigned int bestProcessGen = 0;
int bestProcessBusyness = 0;

Process *fallbackProcess = nullptr;
unsigned long long fallbackProcessStartTime = 0;
unsigned int fallbackProcessGen = 0;
int fallbackProcessBusyness = 0;

unsigned int size = enabledProcessBusynessLevels.size();
const int *enabledProcessBusynessLevels = &this->enabledProcessBusynessLevels[0];

for (i = 0; i < size; i++) {
if (leastBusyProcessIndex == -1 || enabledProcessBusynessLevels[i] < lowestBusyness) {
leastBusyProcessIndex = i;
lowestBusyness = enabledProcessBusynessLevels[i];
for (unsigned int i = 0; i < size; i++) {
Process *process = enabledProcesses.at(i).get();
unsigned int gen = process->generation;
unsigned long long startTime = process->spawnStartTime;
int busyness = enabledProcessBusynessLevels[i];
bool totallyBusy = process->isTotallyBusy();
if (totallyBusy && bestProcess == nullptr) {
CamJN marked this conversation as resolved.
Show resolved Hide resolved
if (fallbackProcess == nullptr ||
gen > fallbackProcess->generation ||
(gen == fallbackProcessGen && startTime < fallbackProcessStartTime) ||
(gen == fallbackProcessGen && startTime == fallbackProcessStartTime && busyness < fallbackProcessBusyness)
) {
fallbackProcess = process;
fallbackProcessGen = gen;
fallbackProcessBusyness = busyness;
fallbackProcessStartTime = startTime;
}
} else if (!totallyBusy && (bestProcess == nullptr ||
gen > bestProcess->generation ||
(gen == bestProcessGen && startTime < bestProcessStartTime) ||
(gen == bestProcessGen && startTime == bestProcessStartTime && busyness < bestProcessBusyness)
)) {
bestProcess = process;
bestProcessGen = gen;
bestProcessBusyness = busyness;
bestProcessStartTime = startTime;
}
}
return enabledProcesses[leastBusyProcessIndex].get();
if (bestProcess == nullptr) {
return fallbackProcess;
}
return bestProcess;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/agent/Core/ApplicationPool/Group/SessionManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ Group::RouteResult
Group::route(const Options &options) const {
if (OXT_LIKELY(enabledCount > 0)) {
if (options.stickySessionId == 0) {
Process *process = findEnabledProcessWithLowestBusyness();
Process *process = findBestEnabledProcess();
if (process->canBeRoutedTo()) {
return RouteResult(process);
} else {
return RouteResult(NULL, true);
}
} else {
Process *process = findProcessWithStickySessionIdOrLowestBusyness(
Process *process = findBestProcessPreferringStickySessionId(
options.stickySessionId);
if (process != NULL) {
if (process->canBeRoutedTo()) {
Expand All @@ -84,7 +84,7 @@ Group::route(const Options &options) const {
}
}
} else {
Process *process = findProcessWithLowestBusyness(disablingProcesses);
Process *process = findBestProcess(disablingProcesses);
if (process->canBeRoutedTo()) {
return RouteResult(process);
} else {
Expand Down Expand Up @@ -310,7 +310,7 @@ Group::get(const Options &newOptions, const GetCallback &callback,
assert(m_spawning || restarting() || poolAtFullCapacity());

if (disablingCount > 0 && !restarting()) {
Process *process = findProcessWithLowestBusyness(disablingProcesses);
Process *process = findBestProcess(disablingProcesses);
assert(process != NULL);
if (!process->isTotallyBusy()) {
return newSession(process, newOptions.currentTime);
Expand Down
2 changes: 0 additions & 2 deletions src/agent/Core/ApplicationPool/Pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@

#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <sstream>
#include <iomanip>
#include <boost/thread.hpp>
#include <boost/bind/bind.hpp>
#include <boost/shared_ptr.hpp>
Expand Down
13 changes: 10 additions & 3 deletions src/agent/Core/ApplicationPool/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ typedef boost::container::vector<ProcessPtr> ProcessList;
*/
class Process {
public:
friend class Group;

static const unsigned int MAX_SOCKETS_ACCEPTING_HTTP_REQUESTS = 3;

private:
Expand Down Expand Up @@ -383,6 +385,10 @@ class Process {

/** Last time when a session was opened for this Process. */
unsigned long long lastUsed;
/** Which generation of app processes this one belongs to,
inherited from the app group, incremented when a restart
is initiated*/
const unsigned int generation;
/** Number of sessions currently open.
* @invariant session >= 0
*/
Expand Down Expand Up @@ -445,8 +451,7 @@ class Process {
/** Collected by Pool::collectAnalytics(). */
ProcessMetrics metrics;


Process(const BasicGroupInfo *groupInfo, const Json::Value &args)
Process(const BasicGroupInfo *groupInfo, const unsigned int gen, const Json::Value &args)
: info(this, groupInfo, args),
socketsAcceptingHttpRequestsCount(0),
spawnerCreationTime(getJsonUint64Field(args, "spawner_creation_time")),
Expand All @@ -457,6 +462,7 @@ class Process {
refcount(1),
index(-1),
lastUsed(spawnEndTime),
generation(gen),
sessions(0),
processed(0),
lifeStatus(ALIVE),
Expand All @@ -470,7 +476,7 @@ class Process {
indexSocketsAcceptingHttpRequests();
}

Process(const BasicGroupInfo *groupInfo, const SpawningKit::Result &skResult,
Process(const BasicGroupInfo *groupInfo, const unsigned int gen, const SpawningKit::Result &skResult,
const Json::Value &args)
: info(this, groupInfo, skResult),
socketsAcceptingHttpRequestsCount(0),
Expand All @@ -482,6 +488,7 @@ class Process {
refcount(1),
index(-1),
lastUsed(spawnEndTime),
generation(gen),
sessions(0),
processed(0),
lifeStatus(ALIVE),
Expand Down
Loading