#! /bin/sh
#set -xv
######################################################################
#
# Copyright (C) GemTalk Systems 1986-2024.  All Rights Reserved.
#
# Script for starting shared cache warmer gem.
#
# Phase 1:
# gem reads the object table into the shared page cache.
#
# Phase 2: (optional)
# Reads data pages.  Data pages can be read into the shared cache 
# or only into the UNIX file buffer cache.  
# Phase 2 is run if '-d' or '-D; is specified on the command line.
#
# See usage() below or run 'startcachewarmer -h' for detailed 
# command line options
#
#
######################################################################

# must have $GEMSTONE
if [ "a$GEMSTONE" = "a" ]; then
  echo "ERROR: GemStone scripts require a GEMSTONE environment variable."
  echo "       Please set it to the directory where GemStone resides."
  exit 1
fi

# ensure a minimal path
if [ "x$PATH" = "x" ]; then PATH=:/bin:/usr/bin:/usr/ucb; export PATH; fi 

# error control - do not allow hup
trap '' 1

comid="startcachewarmer"

usage() {
  cat <<EOF
Usage:
$comid [-d|-D] [-h] [-e gemConfigPath ] [-l cacheFullLimit] [-L logPath] [-n numSessions] 
       [-s stoneName] [-w writeInterval] [-W] 
       [-H stoneHost [-M midCacheHost [-C midCacheSizeKb] [-N midCacheMaxProcs] ] ] 

  -C <midCacheSize>
        size of the mid-level cache in KB (default 75000).  Only used if the 
        -M option is also specified and the mid-level cache does not exist.
  -d    load data files into the shared cache in page order id.  Does not apply if the 
        file /opt/gemstone/locks/<stoneName><hostid>.workingSet.lz4 exists.  
  -D    read data pages into the UNIX file system buffers, but not into the SPC. Does 
        not apply if the file /opt/gemstone/locks/<stoneName><hostid>.workingSet.lz4 exists. 
  -e <gemConfigPath> path to a gem config file, default is gem.conf in current directory ,
        useful for specifing shared cache config if the warmer is to start a remote or mid cache.
  -h    display this message and exit. 
  -H <stoneHost> host name or IP address where the stone is running.  ONLY to be used when 
        warming a remote shared page cache.
  -l <cacheFullLimit> stop cache warming if the free frame count falls below <cacheFullLimit>. 
        Use -1 to have system compute a default value. Use 0 to force cache warming 
        to continue even if the shared cache is full.  (default: -1)
  -L <logPath> path to a writable log file directory. (default: currentDirectory)
  -M <midCacheHost>  host name or IP address where the mid-level cache is running or will 
        be created. The -H option must also be specified with this option.
  -n <numSessions> number of warmer threads to use. (default: numberOfCpus + numExtents)
        There is always one additional "master" thread allocated. The warmer will use
        fewer threads and issue a warming if not enough sessions are available. 
  -N <midCacheMaxProcs>
        maximum number of processes that can use the mid-level cache (default: 50).
        Only used if the -M option is also specified and mid-level cache does not exist.
  -s <stoneName> name of running stone. (default: 'gs64stone')
  -w <writeInterval>
        Instruct the shared page cache monitor to write the ids of all data pages in the 
        shared cache to the working set file, at the given interval in minutes.  
        A value of 0 means write the file only when the stone is shutdown or killed. 
        The working set is written to /opt/gemstone/locks/<stoneName><hostid>.workingSet.lz4 
  -W    wait for cache warming gems to exit before exiting this script (default: 
        spawn gem in the background and exit immediately)

Load pages into the shared page cache.  The object table and dependency map pages are always 
loaded; data pages are loaded based on the -d flag and the presence of the working set file.

If the working set file /opt/gemstone/locks/<stoneName><hostid>.workingSet.lz4 exists,
the valid data pages in this file are loaded.
If it does not exist, then behavior depends on the use of the -d and -D options.
If the -d option is specified, all data pages in page order are loaded.
With the -D option, data pages are loaded into the OS file buffer but not into the shared page cache.
The -d and -D flags are mutually exclusive, if both are specified, then the later one is used.

If a remote SPC is specfied to be warmed (via -H), it will be created if it does not exist. 
If a mid-level cache host name or IP address is specified (via -M) and it does not exist,
it will be created, using the -C and -N option arguments. 

EOF
  exit $status
}


# ---
# defaults
# ---
readDataPagesOpCode="0"
cacheFullLimit=-1
numSessions=0
stone="gs64stone"
waitForGem=0
logPath=`pwd`
midCacheHost=""
midCacheSize=0
midCacheProcs=0
gemConf=""
wsInterval=-1
# ---
# process command line
# ---
while getopts "C:dDe:hH:l:L:M:n:N:s:w:W" opt; do
  case $opt in
    C) midCacheSize=$OPTARG ;;
    d) readDataPagesOpCode="1" ;;
    D) readDataPagesOpCode="2" ;;
    e) gemConf="-e $OPTARG" ;;
    h) status=0; usage ;;
    H) stoneHost=$OPTARG ;; # Fix 44298
    l) cacheFullLimit=$OPTARG ;;
    L) logPath=$OPTARG ;;
    M) midCacheHost=$OPTARG ;;
    N) midCacheProcs=$OPTARG ;;
    n) numSessions=$OPTARG ;;
    s) stone=$OPTARG ;;
    w) wsInterval=$OPTARG ;;
    W) waitForGem=1 ;;
# Request 38663: accept a directory for log files
    \?) status=1; usage ;;
  esac
done

if [ $numSessions -lt 0 ]; then
  echo "$comid [ERROR]: numSessions is too small."
  exit 1
fi

if [ $cacheFullLimit -lt -1 ]; then
  echo "$comid [ERROR]: cacheFullLimit is too small."
  exit 1
fi

if [ ! -d $logPath ]; then
  echo "$comid [ERROR]: $logPath is not a directory."
  exit 1
fi

if [ ! -w $logPath ]; then
  echo "$comid [ERROR]: $logPath is not writable."
  exit 1
fi

if [ "$midCacheHost" = "" ]; then
  # No mid cache specified, so -C and -N shouldn't be here.  Make sure.
  if [ $midCacheProcs -ne 0 -o $midCacheSize -ne 0 ]; then
    echo "$comid [ERROR]: -C and -N options may not be specified unless -M option is also specified."
    exit 1
  fi
fi

MID_ARGS=""
MID_HOST_PRINTED="(none)"
# Fix 44298 - support for warmers on remote caches
if [ "$stoneHost" = "" ]; then # Stone running on our host
  if [ "$midCacheHost" != "" ]; then
    echo "$comid [ERROR]: Cannot use a mid-level cache when running cache warmers on the stone's cache."
    exit 1
  fi
  STN_NRS=$stone
  STN_WS_NRS=$stone
  STN_HOST_PRINTED=$hostname
else
# We are warming a remote cache.
  STN_NRS="!@$stoneHost!$stone"
  STN_WS_NRS="!@$stoneHost!$stone"
  STN_HOST_PRINTED=$stoneHost
  if [ "$midCacheHost" != "" ]; then
    # User wants warmer to use a mid cache.
    MID_HOST_PRINTED=$midCacheHost
    # Use defaults if values were not given.
    # Gem will check that values are in range.
    if [ $midCacheProcs -eq 0 ]; then
      midCacheProcs=50 
    fi
    if [ $midCacheSize -eq 0 ]; then
      midCacheSize=75000
    fi
    MID_ARGS="-M $midCacheHost -C $midCacheSize -N $midCacheProcs"
  fi
fi

# check to make sure $stone is a running stone
$GEMSTONE/bin/waitstone $STN_WS_NRS -1 >/dev/null 2>&1
if [ $? -ne 0 ]; then
  if [ ! -w $logPath ]; then
    echo "$comid [ERROR]: $stone is not a running stone."
  else
    echo "$comid [ERROR]: $stone is not a running stone on host $stoneHost"
  fi
  exit 1
fi

slash="/"
testFile=${logPath}${slash}foo
touch $testFile >/dev/null 2>&1
if [ $? -eq 0 ]; then
  rm -f $testFile >/dev/null 2>&1
else
  slash=""
  testFile=${logPath}${slash}foo
  touch $testFile >/dev/null 2>&1
  if [ $? -ne 0 ]; then
    echo "$comid [ERROR]: cannot create file in directory $logPath"
    exit 1
  fi
  rm -f $testFile >/dev/null 2>&1
fi

logName=${logPath}${slash}${stone}_cachewarmer.log
rm -f $logName >/dev/null 2>&1
GEMSTONE_CHILD_LOG=$logName
export GEMSTONE_CHILD_LOG

wsIntervalStr=$wsInterval
if [ $wsInterval -lt 0 ]; then
  wsIntervalStr='(disabled)'
fi
cat <<EOF
Cache warming configuration:
              Stone: $stone
          StoneHost: $STN_HOST_PRINTED
           Sessions: $numSessions
    Read data pages: $readDataPagesOpCode
     CacheFullLimit: $cacheFullLimit
     Mid cache host: $MID_HOST_PRINTED
     Mid cache size: $midCacheSize
    Mid cache procs: $midCacheProcs
    wsWriteInterval: $wsIntervalStr
        LogFileName: $logName
         WaitForGem: $waitForGem
Spawning cache warming gem...
EOF

# The default behaviour is to delete this process's log file if it exits
# normally. If you want to keep this process's log file even on normal exit
# then uncomment the following
# GEMSTONE_KEEP_LOG=1 ; export GEMSTONE_KEEP_LOG
# use runcachewarmergem script to start it
 
# use runcachewarmergem script to start it

$GEMSTONE/sys/runcachewarmergem $gemConf cachewarmer -s $STN_NRS -n $numSessions -l $cacheFullLimit \
-o $readDataPagesOpCode -w $wsInterval $MID_ARGS -L $logName > $logName 2>&1 &
thisPid=$!
echo "$comid [Info]: Started cache warmer gem with process id: $thisPid."
 if [ $waitForGem -eq 1 ]; then
  echo "$comid [Info]: waiting for warmer gem with process ID $thisPid to finish..."
  wait $thisPid
  if [ $? -eq 0 ]; then
    echo "CacheWarming completed successfully" 
  else
    echo "CacheWarming was not successful, see cachewarmer log for details"
  fi
fi

exit 0
