[SPARK-3795] Heuristics for dynamically scaling executors

This is part of a bigger effort to provide elastic scaling of executors within a Spark application ([SPARK-3174](https://issues.apache.org/jira/browse/SPARK-3174)). This PR does not provide any functionality by itself; it is a skeleton that is missing a mechanism to be added later in [SPARK-3822](https://issues.apache.org/jira/browse/SPARK-3822).

Comments and feedback are most welcome. For those of you reviewing this in detail, I highly recommend doing it through your favorite IDE instead of through the diff here.
The doc, function name, and log message here seem a little off because this is also called in the case where an executor is actually removed due to a timeout (i.e. not only when it's cancelled). What about calling this "clearRemoveTimer"? In general it's not great to discuss the invocation scenario of a function in the doc, because it can become out of date, as it did here.