milicagnjatovic / master_rad

1 stars 0 forks source link

Time constraint #7

Open milicagnjatovic opened 9 months ago

milicagnjatovic commented 9 months ago

Problem: ograničiti vreme izvršvanja generisanja fajla sa rešenjem i izvršavanja studentskog upita.

milicagnjatovic commented 9 months ago

Generate solution

Originalna ideja:

Skripti se prosleđuje fajl u formatu:

taskid_1
quey_1
taskid_2
quey_2
...

Izvrši se konektovanje na bazu podataka. Čitaju se dve po dve linije i izvršava naredno: db2 "$query" | sed '/^$/d' > "results/$task_id" Važno je da se svi upiti redom izvršavaju sa jednom konekcijom.

Komanda timeout t command prekida izvršavanje command nakom t sekundi. Ukoliko se izvršvanje završilo ranije nema čekanja. Timeout će pokrenuti novi child proces koji nema uspostavljenu konekciju na bazu pa jednostavno izvršavanje nije moguće: timeout 5s db2 "$query" | sed '/^$/d' > "results/$task_id" Mogli bi ulančati komandu za povezivanje na bazu u timeout, ali kako je ovde ideja jednom konekcijom izvršiti sve upite iskoristićemo narednu alternativu, a timeout će biti iskorišćen za proveru efikasnoti studnetskog koda.

Time constrain unapređenje:

  db2 "$query" | sed '/^$/d' > "results/$task_id" &   # Proces za izvršavanje upita ide u pozadinu i ne blokira dalje izvršavanje
  pid=$!  # id prethodnog procesa u pozadini

# petlja koja proverava da li je upit izvršen na svake 0.02 sekunde
  for ((i=0; i<200; i++)); do
    sleep 0.02s
    if ! ps -p $pid > /dev/null; then # provera da li je proces gotov
      skip=false
      break
    fi
  done

  if $skip; then # ukoliko proces nije završen nakon datog vremena gasi se i javalj poruka da je prekoračeno vremensko ograničenje 
    kill $pid
    echo "$task_id | Time limit exceeded"
    continue
  fi

Na osnovu vremena izvršavanja svakog upita bez ograničenja su dobijene naredne vrednosti:

Pri izboru vremenskog koraka je razmotreno vreme potrebno za generisanje 99 upita (od čega 10 sa ispita): Vremenski korak Vreme izvršavanja Komentar
nema 12.97s Nije opcija jer može da blokira sistem ako se pošalje upit koji se predugo izvršava
1s 104.53s Predug opseg, većina upita će se izvršiti dosta pre prve provere
0.5s 57.60s Vreme izvršavanja je skoro 6 puta duže nego bez ograničenja, vreme čekanja je predugo
0.2s 30.98s
0.1s 21.89s
0.05s 16.41s Na osnovu medijane bi se zaključilo da je ovo idelan granica jer bi se polovina upita izvršila pre prve promene
0.03s 15.16s
0.02s 14.43s Najmanje vreme izvršavanja
0.01s 14.46s Vreme je nešto duže jer se provere izvršavaju previše često

Ovako dato ograničenje ima smisla ukoliko će vreme koje se provodi u čekanju biti manje od vremena potrebnog za izvršavanje svih koenkcija na bazu.

Još jedna važna napomena je da sa jednom komenkcijom u jednom trenutku može da se izvršava samo jedan upit, pa sleep i izvršavanje upita u pozadini ne daju dobre rezulatet. Izvršavanje koda na takav način ja dalo nepredvidive rezulatet. Prilikom generisanja 12 upita, od kojih se dva izvršavaju sporije, dok se poslednji izvršava brzo (select * iz manje tabele) u tri izvršavanja su dobijeni naredni rezultati:

Još jedno razmatranje bi bilo da li vremenski korak da se menja vremenom, na primer počeni korak 0.05 sekundi, pa da se dalje proverava na 0.01 sekundu.

Na kraju treba imati vidu da se generisanje rezultujućih fajlova neće izvršavati veoma često i da nije prioritet da se izvrši najbrže moguće.

milicagnjatovic commented 9 months ago

Check solution

U prethodnom komenataru je razmotrena komanda timeout koja će ovde biti iskorišćena. U procesu koji pokreće timeout je potrebno izvršiti konekciju na bazu podatak:

timeout 5 bash -c "
  db2 connect to stud2020 > /dev/null;
  db2 "'$query'" | sed '/^$/d' > '$user_path' "

Ovde se javlja problem sa prosleđivanjem argumenata procesu jer je potrebno komande staviti između jednostrukih ili dvostrukih navodnika, dok upiti koriste i jednostruke i dvostruke navodnike. Provera:

echo '-------------'
echo "$query"
echo '-------------'
timeout 5 bash -c "
  echo \"$query\"
  echo '-------------'
  echo '$query'
  echo '-------------'

Izlaz:

-------------
SELECT ime || ' ' || prezime "ime i prezime" from da.dosije # navodnici su u redu
-------------
SELECT ime || ' ' || prezime ime i prezime from da.dosije # fale navodnici za ime i prezime
-------------
SELECT ime ||   || prezime "ime i prezime" from da.dosije # fale navodnici za razmak
-------------

Jedna ideja bi bila da se upit upiše u fajl, pa da timeout proces izvrši iz fajla. Ali onda bi se nepotrebno pravio fajl.

Druga ideja je pravljenje funkcije koja bi se pozivala iz timeout. U tom slučaju je funkciju i argumente potrebno exportovati da bi bili vidljivi u child procesima:

function execute_query() {
  db2 connect to stud2020 > /dev/null; # na izlat treba da ide samo poruka o izvršavanju, ne rezultat konekcije
  db2 "$query" | sed '/^$/d' > "$user_path"
}

export -f execute_query
export query="$query"
export user_path="$user_path"

# funkcija se izvršava 5 sekundi, ukoliko se ne završi za to vreme prekida se izvršavanje
timeout 5 bash -c "execute_query"

# if last command failed
if [ $? -ne 0 ]; then
  echo "User error | Time limit exceeded"
  if [ -e "$user_path"  ]; then
    rm "$user_path"
    exit 1
  fi
fi