Memory pressure¶
When faced with the risk of running low on memory, you know that some of your programs’ variables are cache. They can be discarded, dropped on the floor, to be recomputed later.
Problem is, that they need a trigger to do it. Memory pressure management from Satella solves that problem.
-
satella.instrumentation.memory.
get_size
(obj, seen=None) → int¶ Recursively finds the total size of an object (object + it’s components).
Parameters: obj – object to measure Returns: size in bytes of the object and all of it’s subcomponents Raises: RuntimeError – when ran on PyPy
Defining severity levels¶
To define a severity level, use the following classes:
-
class
satella.instrumentation.memory.
GlobalAbsoluteValue
(value: int)¶ If free memory globally falls below this many bytes, given severity level starts
-
class
satella.instrumentation.memory.
GlobalRelativeValue
(value: int)¶ If percentage of global free memory falls below this much percents, given severity level starts
-
class
satella.instrumentation.memory.
LocalAbsoluteValue
(value: int)¶ If free memory falls below this many bytes from what the program can maximally consume this severity level starts
-
class
satella.instrumentation.memory.
LocalRelativeValue
(value: int)¶ If percentage of memory available to this process in regards to what the program can maximally consume falls below this level, given severity level starts
Here you can either provide a callable or override the can_fire
method
-
class
satella.instrumentation.memory.
CustomCondition
(callable_: Callable[[], bool])¶ A custom condition. Condition that is true if attached callable/0 returns True.
Parameters: callable – callable to call upon asking whether this condition is valid. This should be relatively cheap to compute. -
can_fire
(local_memory_data, local_maximum_consume: Optional[int]) → bool¶ Has this severity level been reached?
-
You can combine them with following operators:
-
class
satella.instrumentation.memory.
All
(*conditions)¶ This is true if all arguments are True
-
class
satella.instrumentation.memory.
Any
(*conditions)¶ This is true if one of the arguments is True
-
class
satella.instrumentation.memory.
Not
(condition: satella.instrumentation.memory.conditions.BaseCondition)¶ True only if provided condition is false
Then, you make a list out of them. This list, with indices counted from 1, signals what condition needs to be true for the program to enter given severity level.
Handlers¶
It is impossible to go from severity level 1 to say 3 without hitting 2. 2 will be hit by the way, the manager will call any handlers that are in the way. Note that severity levels are concurrent - for example, level 1 coexists with level 2, and if level 2 is in effect, that means that level 1 is still in effect. You can register your handlers here:
-
class
satella.instrumentation.memory.
MemoryPressureManager
(maximum_available: Optional[int] = None, severity_levels: List[satella.instrumentation.memory.conditions.BaseCondition] = None, check_interval: Union[str, int] = 10, log_transitions: bool = True)¶ Manager of the memory pressure.
The program is in some severity state. The baseline state is 0, meaning everything’s OK.
Please note that it is sufficient to instantiate this class for the thread to run.
Eg.
>>> mt = MemoryPressureManager(maximum_available=4*GB, severity_levels=[GlobalRelativeValue(20), >>> GlobalRelativeValue(10)]) >>> @mt.register_on_severity(1) >>> def trigger_a(): >>> print('80% consumption of memory exceeded') >>> @mt.register_on_severity(2) >>> def trigger_b(): >>> print('90% consumption of memory exceeded')
As well, this object is a singleton.
Parameters: - maximum_available – maximum amount of memory that this program can use
- severity_levels – this defines the levels of severity. A level is reached when program’s consumption is other this many percent of it’s maximum_available amount of memory.
- check_interval – amount of seconds of pause between consecutive checks, or a time string
- log_transitions – whether to log to logger when a transition takes place
Variables: severity_level – current severity level (int) 0 means memory is OK, 1 and more means memory is progressively more limited
-
calculate_severity_level
() → int¶ This returns a severity level. 0 is the baseline severity level.
-
loop
() → None¶ Override me!
-
static
register_on_entered_severity
(severity: int)¶ Register this handler to fire on entered a particular severity level.
This means that situation has gotten worse.
Use like this:
>>> MemoryPressureManager.register_on_entered_severity(1) >>> def entered_severity_one(): >>> print('Entered memory severity level 1')
Parameters: severity – severity level to react to
-
static
register_on_left_severity
(severity: int)¶ Register a handler to be called when given severity level is left. This means that we have advanced to a lower severity level.
>>> MemoryPressureManager.register_on_left_severity(1) >>> def entered_severity_one(): >>> print('Memory comsumption no longer 1')
Parameters: severity – severity level to leave
-
static
register_on_memory_normal
(fun: Callable) → satella.coding.concurrent.callablegroup.CancellableCallback¶ Register this handler to fire when memory state falls back to 0.
This will be fired once, once memory state falls back to normal.
Parameters: fun – callable to register Returns: a CancellableCallback under this callback is registered
-
static
register_on_remaining_in_severity
(severity: int, call_no_more_often_than: int = 0)¶ Register this handler to fire on remaining in a particular severity level. Use like this:
>>> MemoryPressureManager.register_on_remaining_in_severity(0, 30) >>> def entered_severity_one(): >>> print('Memory comsumption OK. I am called no more often than each 30 seconds')
Parameters: - severity – severity level
- call_no_more_often_than – call no more often than this amount of seconds
-
resume
()¶ Resume the operation of this thread
-
stop
()¶ Stop this thread from operating
install_force_gc_collect¶
If you want, you can install a GC handler that will force a complete GC collection upon entering given severity level.
-
satella.instrumentation.memory.
install_force_gc_collect
(severity_level: int = 1) → None¶ Install a default first severity level handler that forces a GC collection
Parameters: severity_level – severity level on which to call
Dumping memory information¶
-
satella.instrumentation.memory.
dump_memory_on
(output: TextIO = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>)¶ Dump statistics about current Python memory usage to target stream.
Each Python object will be printed, along with a breakdown of most types and their total usage.
Make sure you have enough memory to generate a breakdown. You can preallocate something at the start for example.
Warning
This will return size of 0 on PyPy
Parameters: output – output, default is stderr
-
satella.instrumentation.memory.
install_dump_memory_on
(signal_number, output: TextIO = <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>)¶ Instruct Python to dump all frames onto output, along with their local variables upon receiving given signal
Parameters: - signal_number – number of the signal
- output – output