Add the scheduling module
Three replicas of one application against one PostgreSQL database, proving duplicate @Scheduled execution and then removing it with ShedLock: 24 executions where 8 were due, then 7 for 7 ticks. Also measured: @SchedulerLock without @EnableSchedulerLock does nothing and warns about nothing; spring.task.scheduling.pool.size=1 does not starve a fixedRate job but delays it and fires 35 of 40 executions in a burst; and a node whose clock is 40 seconds fast takes a live lock unless the provider uses usingDbTime().
This commit is contained in:
47
scheduling/scripts/postgres.sh
Executable file
47
scheduling/scripts/postgres.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts a throwaway PostgreSQL 14 on port 55432 with no Docker and no root.
|
||||
#
|
||||
# On a Debian/Ubuntu machine the .deb packages can simply be downloaded and unpacked into a
|
||||
# prefix; PostgreSQL does not need to be installed system-wide and refuses to run as root anyway.
|
||||
# This is how docs/output/ was produced. If you have Docker, docker-compose.yml is easier.
|
||||
#
|
||||
# scripts/postgres.sh start|stop|psql
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
PREFIX="$PWD/target/pg"
|
||||
PGROOT="$PREFIX/root"
|
||||
PGDATA="$PREFIX/data"
|
||||
export PATH="$PGROOT/usr/lib/postgresql/14/bin:$PATH"
|
||||
export LD_LIBRARY_PATH="$PGROOT/usr/lib/x86_64-linux-gnu:$PGROOT/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}"
|
||||
|
||||
install_pg() {
|
||||
mkdir -p "$PREFIX/debs" "$PGROOT"
|
||||
( cd "$PREFIX/debs"
|
||||
for p in postgresql-14 postgresql-client-14 postgresql-common postgresql-client-common \
|
||||
libpq5 libllvm14 libicu70 libssl3 libxslt1.1 libxml2 libedit2 libbsd0 libmd0 \
|
||||
zlib1g liblz4-1 libzstd1 libtinfo6 libncurses6 libcom-err2 libkrb5-3 libk5crypto3 \
|
||||
libkrb5support0 libgssapi-krb5-2 libkeyutils1 libsasl2-2 libldap-2.5-0 libgnutls30 \
|
||||
libnettle8 libhogweed6 libgmp10 libp11-kit0 libtasn1-6 libidn2-0 libunistring2 libffi8; do
|
||||
apt-get download "$p" >/dev/null 2>&1 || true
|
||||
done
|
||||
for d in *.deb; do dpkg -x "$d" "$PGROOT" 2>/dev/null || true; done )
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
[ -x "$PGROOT/usr/lib/postgresql/14/bin/initdb" ] || install_pg
|
||||
if [ ! -d "$PGDATA" ]; then
|
||||
initdb -D "$PGDATA" -U app --auth=trust -E UTF8 >/dev/null
|
||||
fi
|
||||
pg_ctl -D "$PGDATA" -o "-p 55432 -k $PREFIX -c listen_addresses=127.0.0.1" \
|
||||
-l "$PREFIX/pg.log" start >/dev/null 2>&1 || true
|
||||
for _ in $(seq 1 30); do pg_isready -h 127.0.0.1 -p 55432 >/dev/null 2>&1 && break; sleep 1; done
|
||||
psql -h 127.0.0.1 -p 55432 -U app -d postgres -tc \
|
||||
"select 1 from pg_database where datname='shedlockdemo'" | grep -q 1 || \
|
||||
psql -h 127.0.0.1 -p 55432 -U app -d postgres -q -c "create database shedlockdemo"
|
||||
pg_isready -h 127.0.0.1 -p 55432
|
||||
;;
|
||||
stop) pg_ctl -D "$PGDATA" stop -m fast >/dev/null 2>&1 || true ;;
|
||||
psql) shift; psql -h 127.0.0.1 -p 55432 -U app -d shedlockdemo "$@" ;;
|
||||
*) echo "usage: $0 start|stop|psql" >&2; exit 2 ;;
|
||||
esac
|
||||
16
scheduling/scripts/run-all.sh
Executable file
16
scheduling/scripts/run-all.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerates every file under docs/output/. Takes about three minutes: two three-replica runs of
|
||||
# 30 seconds each plus the test suite, and three JVMs need roughly ten seconds to start on a
|
||||
# small box.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
mkdir -p docs/output
|
||||
scripts/postgres.sh start >/dev/null
|
||||
|
||||
mvn -B -q -DskipTests package
|
||||
mvn -B test 2>&1 | grep -E 'Running |Tests run:|BUILD ' > docs/output/tests.txt
|
||||
|
||||
scripts/three-replicas.sh unlocked > docs/output/three-replicas-unlocked.txt
|
||||
scripts/three-replicas.sh locked > docs/output/three-replicas-locked.txt
|
||||
|
||||
echo "regenerated:"; ls -1 docs/output
|
||||
52
scheduling/scripts/three-replicas.sh
Executable file
52
scheduling/scripts/three-replicas.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts three replicas of the application against one database, lets the schedule tick, and
|
||||
# tallies who ran the job.
|
||||
#
|
||||
# scripts/three-replicas.sh unlocked # no @EnableSchedulerLock: every replica runs it
|
||||
# scripts/three-replicas.sh locked # ShedLock with usingDbTime()
|
||||
# scripts/three-replicas.sh appclock 40 # ShedLock without usingDbTime(), replica-3 40s fast
|
||||
#
|
||||
# Environment:
|
||||
# RUN_FOR seconds to let the schedule tick (default 30; three JVMs need ~10s to start)
|
||||
# JOB_DURATION ISO-8601 duration the job sleeps for (default PT0.2S)
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
PROFILE="${1:-unlocked}"
|
||||
SKEW_SECONDS="${2:-0}"
|
||||
RUN_FOR="${RUN_FOR:-30}"
|
||||
JOB_DURATION="${JOB_DURATION:-PT0.2S}"
|
||||
JAR=target/scheduling-1.0.jar
|
||||
PSQL=(scripts/postgres.sh psql)
|
||||
|
||||
[ -f "$JAR" ] || mvn -B -q -DskipTests package
|
||||
|
||||
"${PSQL[@]}" -q -c "delete from job_execution" -c "delete from shedlock" >/dev/null 2>&1 || true
|
||||
|
||||
pids=()
|
||||
for n in 1 2 3; do
|
||||
skew=()
|
||||
if [ "$SKEW_SECONDS" != "0" ] && [ "$n" = "3" ]; then
|
||||
skew=(-Dclock.skew="PT${SKEW_SECONDS}S")
|
||||
fi
|
||||
java -Dspring.profiles.active="$PROFILE" -DINSTANCE_ID="replica-$n" \
|
||||
-Djob.duration="$JOB_DURATION" "${skew[@]}" \
|
||||
-jar "$JAR" > "target/replica-$n.log" 2>&1 &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
sleep "$RUN_FOR"
|
||||
for p in "${pids[@]}"; do kill "$p" 2>/dev/null || true; done
|
||||
wait 2>/dev/null || true
|
||||
|
||||
echo "profile=$PROFILE replicas=3 run-for=${RUN_FOR}s job rate=3s job duration=$JOB_DURATION replica-3 clock skew=${SKEW_SECONDS}s"
|
||||
echo
|
||||
echo "-- every execution, in order --"
|
||||
"${PSQL[@]}" -c "select instance_id, to_char(started_at,'HH24:MI:SS.MS') as started_at from job_execution order by started_at"
|
||||
echo "-- executions per replica --"
|
||||
"${PSQL[@]}" -c "select instance_id, count(*) from job_execution group by instance_id order by instance_id"
|
||||
OVERLAP="b.id > a.id and b.instance_id <> a.instance_id and b.started_at < a.started_at + interval '$JOB_DURATION'"
|
||||
echo "-- pairs of executions that overlapped (a second replica started while the first was still working) --"
|
||||
"${PSQL[@]}" -c "select count(*) as overlapping_pairs from job_execution a join job_execution b on $OVERLAP"
|
||||
"${PSQL[@]}" -c "select a.instance_id as first, to_char(a.started_at, 'HH24:MI:SS.MS') as first_at, b.instance_id as second, to_char(b.started_at, 'HH24:MI:SS.MS') as second_at from job_execution a join job_execution b on $OVERLAP order by a.started_at limit 6"
|
||||
echo "-- the lock row --"
|
||||
"${PSQL[@]}" -c "select name, locked_by, to_char(locked_at,'HH24:MI:SS.MS') as locked_at, to_char(lock_until,'HH24:MI:SS.MS') as lock_until from shedlock"
|
||||
Reference in New Issue
Block a user