Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2895428
PhutilDaemonPool.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
9 KB
Referenced Files
None
Subscribers
None
PhutilDaemonPool.php
View Options
<?php
final
class
PhutilDaemonPool
extends
Phobject
{
private
$properties
=
array
(
)
;
private
$commandLineArguments
;
private
$overseer
;
private
$daemons
=
array
(
)
;
private
$argv
;
private
$lastAutoscaleUpdate
;
private
$inShutdown
;
private
function
__construct
(
)
{
// <empty>
}
public
static
function
newFromConfig
(
array
$config
)
{
PhutilTypeSpec
::
checkMap
(
$config
,
array
(
'class'
=>
'string'
,
'label'
=>
'string'
,
'argv'
=>
'optional list<string>'
,
'load'
=>
'optional list<string>'
,
'log'
=>
'optional string|null'
,
'pool'
=>
'optional int'
,
'up'
=>
'optional int'
,
'down'
=>
'optional int'
,
'reserve'
=>
'optional int|float'
,
)
)
;
$config
=
$config
+
array
(
'argv'
=>
array
(
)
,
'load'
=>
array
(
)
,
'log'
=>
null
,
'pool'
=>
1
,
'up'
=>
2
,
'down'
=>
15
,
'reserve'
=>
0
,
)
;
$pool
=
new
self
(
)
;
$pool
->
properties
=
$config
;
return
$pool
;
}
public
function
setOverseer
(
PhutilDaemonOverseer
$overseer
)
{
$this
->
overseer
=
$overseer
;
return
$this
;
}
public
function
getOverseer
(
)
{
return
$this
->
overseer
;
}
public
function
setCommandLineArguments
(
array
$arguments
)
{
$this
->
commandLineArguments
=
$arguments
;
return
$this
;
}
public
function
getCommandLineArguments
(
)
{
return
$this
->
commandLineArguments
;
}
private
function
shouldShutdown
(
)
{
return
$this
->
inShutdown
;
}
private
function
newDaemon
(
)
{
$config
=
$this
->
properties
;
if
(
count
(
$this
->
daemons
)
)
{
$down_duration
=
$this
->
getPoolScaledownDuration
(
)
;
}
else
{
// TODO: For now, never scale pools down to 0.
$down_duration
=
0
;
}
$forced_config
=
array
(
'down'
=>
$down_duration
,
)
;
$config
=
$forced_config
+
$config
;
$config
=
array_select_keys
(
$config
,
array
(
'class'
,
'log'
,
'load'
,
'argv'
,
'down'
,
)
)
;
$daemon
=
PhutilDaemonHandle
::
newFromConfig
(
$config
)
->
setDaemonPool
(
$this
)
->
setCommandLineArguments
(
$this
->
getCommandLineArguments
(
)
)
;
$daemon_id
=
$daemon
->
getDaemonID
(
)
;
$this
->
daemons
[
$daemon_id
]
=
$daemon
;
$daemon
->
didLaunch
(
)
;
return
$daemon
;
}
public
function
getDaemons
(
)
{
return
$this
->
daemons
;
}
public
function
getFutures
(
)
{
$futures
=
array
(
)
;
foreach
(
$this
->
getDaemons
(
)
as
$daemon
)
{
$future
=
$daemon
->
getFuture
(
)
;
if
(
$future
)
{
$futures
[
]
=
$future
;
}
}
return
$futures
;
}
public
function
didReceiveSignal
(
$signal
,
$signo
)
{
switch
(
$signal
)
{
case
PhutilDaemonOverseer
::
SIGNAL_GRACEFUL
:
case
PhutilDaemonOverseer
::
SIGNAL_TERMINATE
:
$this
->
inShutdown
=
true
;
break
;
}
foreach
(
$this
->
getDaemons
(
)
as
$daemon
)
{
switch
(
$signal
)
{
case
PhutilDaemonOverseer
::
SIGNAL_NOTIFY
:
$daemon
->
didReceiveNotifySignal
(
$signo
)
;
break
;
case
PhutilDaemonOverseer
::
SIGNAL_RELOAD
:
$daemon
->
didReceiveReloadSignal
(
$signo
)
;
break
;
case
PhutilDaemonOverseer
::
SIGNAL_GRACEFUL
:
$daemon
->
didReceiveGracefulSignal
(
$signo
)
;
break
;
case
PhutilDaemonOverseer
::
SIGNAL_TERMINATE
:
$daemon
->
didReceiveTerminateSignal
(
$signo
)
;
break
;
default
:
throw
new
Exception
(
pht
(
'Unknown signal "%s" ("%d").'
,
$signal
,
$signo
)
)
;
}
}
}
public
function
getPoolLabel
(
)
{
return
$this
->
getPoolProperty
(
'label'
)
;
}
public
function
getPoolMaximumSize
(
)
{
return
$this
->
getPoolProperty
(
'pool'
)
;
}
public
function
getPoolScaleupDuration
(
)
{
return
$this
->
getPoolProperty
(
'up'
)
;
}
public
function
getPoolScaledownDuration
(
)
{
return
$this
->
getPoolProperty
(
'down'
)
;
}
public
function
getPoolMemoryReserve
(
)
{
return
$this
->
getPoolProperty
(
'reserve'
)
;
}
public
function
getPoolDaemonClass
(
)
{
return
$this
->
getPoolProperty
(
'class'
)
;
}
private
function
getPoolProperty
(
$key
)
{
return
idx
(
$this
->
properties
,
$key
)
;
}
public
function
updatePool
(
)
{
$daemons
=
$this
->
getDaemons
(
)
;
foreach
(
$daemons
as
$key
=>
$daemon
)
{
$daemon
->
update
(
)
;
if
(
$daemon
->
isDone
(
)
)
{
$daemon
->
didExit
(
)
;
unset
(
$this
->
daemons
[
$key
]
)
;
if
(
$this
->
shouldShutdown
(
)
)
{
$this
->
logMessage
(
'DOWN'
,
pht
(
'Pool "%s" is exiting, with %s daemon(s) remaining.'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
count
(
$this
->
daemons
)
)
)
)
;
}
else
{
$this
->
logMessage
(
'POOL'
,
pht
(
'Autoscale pool "%s" scaled down to %s daemon(s).'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
count
(
$this
->
daemons
)
)
)
)
;
}
}
}
$this
->
updateAutoscale
(
)
;
}
public
function
isHibernating
(
)
{
foreach
(
$this
->
getDaemons
(
)
as
$daemon
)
{
if
(
!
$daemon
->
isHibernating
(
)
)
{
return
false
;
}
}
return
true
;
}
public
function
wakeFromHibernation
(
)
{
if
(
!
$this
->
isHibernating
(
)
)
{
return
$this
;
}
$this
->
logMessage
(
'WAKE'
,
pht
(
'Autoscale pool "%s" is being awakened from hibernation.'
,
$this
->
getPoolLabel
(
)
)
)
;
$did_wake_daemons
=
false
;
foreach
(
$this
->
getDaemons
(
)
as
$daemon
)
{
if
(
$daemon
->
isHibernating
(
)
)
{
$daemon
->
wakeFromHibernation
(
)
;
$did_wake_daemons
=
true
;
}
}
if
(
!
$did_wake_daemons
)
{
// TODO: Pools currently can't scale down to 0 daemons, but we should
// scale up immediately here once they can.
}
$this
->
updatePool
(
)
;
return
$this
;
}
private
function
updateAutoscale
(
)
{
if
(
$this
->
shouldShutdown
(
)
)
{
return
;
}
// Don't try to autoscale more than once per second. This mostly stops the
// logs from getting flooded in verbose mode.
$now
=
time
(
)
;
if
(
$this
->
lastAutoscaleUpdate
>=
$now
)
{
return
;
}
$this
->
lastAutoscaleUpdate
=
$now
;
$daemons
=
$this
->
getDaemons
(
)
;
// If this pool is already at the maximum size, we can't launch any new
// daemons.
$max_size
=
$this
->
getPoolMaximumSize
(
)
;
if
(
count
(
$daemons
)
>=
$max_size
)
{
$this
->
logMessage
(
'POOL'
,
pht
(
'Autoscale pool "%s" already at maximum size (%s of %s).'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
count
(
$daemons
)
)
,
new
PhutilNumber
(
$max_size
)
)
)
;
return
;
}
$scaleup_duration
=
$this
->
getPoolScaleupDuration
(
)
;
foreach
(
$daemons
as
$daemon
)
{
$busy_epoch
=
$daemon
->
getBusyEpoch
(
)
;
// If any daemons haven't started work yet, don't scale the pool up.
if
(
!
$busy_epoch
)
{
$this
->
logMessage
(
'POOL'
,
pht
(
'Autoscale pool "%s" has an idle daemon, declining to scale.'
,
$this
->
getPoolLabel
(
)
)
)
;
return
;
}
// If any daemons started work very recently, wait a little while
// to scale the pool up.
$busy_for
=
(
$now
-
$busy_epoch
)
;
if
(
$busy_for
<
$scaleup_duration
)
{
$this
->
logMessage
(
'POOL'
,
pht
(
'Autoscale pool "%s" has not been busy long enough to scale up '
.
'(busy for %s of %s seconds).'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
$busy_for
)
,
new
PhutilNumber
(
$scaleup_duration
)
)
)
;
return
;
}
}
// If we have a configured memory reserve for this pool, it tells us that
// we should not scale up unless there's at least that much memory left
// on the system (for example, a reserve of 0.25 means that 25% of system
// memory must be free to autoscale).
// Note that the first daemon is exempt: we'll always launch at least one
// daemon, regardless of any memory reservation.
if
(
count
(
$daemons
)
)
{
$reserve
=
$this
->
getPoolMemoryReserve
(
)
;
if
(
$reserve
)
{
// On some systems this may be slightly more expensive than other
// checks, so we only do it once we're prepared to scale up.
$memory
=
PhutilSystem
::
getSystemMemoryInformation
(
)
;
$free_ratio
=
(
$memory
[
'free'
]
/
$memory
[
'total'
]
)
;
// If we don't have enough free memory, don't scale.
if
(
$free_ratio
<=
$reserve
)
{
$this
->
logMessage
(
'POOL'
,
pht
(
'Autoscale pool "%s" does not have enough free memory to '
.
'scale up (%s free of %s reserved).'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
$free_ratio
,
3
)
,
new
PhutilNumber
(
$reserve
,
3
)
)
)
;
return
;
}
}
}
$this
->
logMessage
(
'AUTO'
,
pht
(
'Scaling pool "%s" up to %s daemon(s).'
,
$this
->
getPoolLabel
(
)
,
new
PhutilNumber
(
count
(
$daemons
)
+
1
)
)
)
;
$this
->
newDaemon
(
)
;
}
public
function
logMessage
(
$type
,
$message
,
$context
=
null
)
{
return
$this
->
getOverseer
(
)
->
logMessage
(
$type
,
$message
,
$context
)
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Jan 19 2025, 21:27 (6 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128929
Default Alt Text
PhutilDaemonPool.php (9 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment