0

So I am trying to dump a lot of tables from 1 server and then restore them back in another server. I want to keep the dump going 1 by 1 and restore them as the dump finishes. I dont want to overwhelm the server by running more than 1 restore operation at the same time. So to achive that i am trying to have a third function which "wait" for a "restore function" to finish before calling another. But I am not able to use the "wait" correctly. The program is not waiting at all.


RESTORE(){

sleep 10 && echo "restore done" &

}


RESTORE_CALLER() {


echo "waiting for any restore with pid ${current_restore_pid}"
wait ${current_restore_pid}


echo "Calling restore"
RESTORE &
current_restore_pid=$!



}


DUMP(){

for ((i=0;i<5;i++));do
echo "dumping "

echo "restore caller"
RESTORE_CALLER &

done

} 

DUMP 

Amar C
  • 374
  • 5
  • 17
  • 1
    If you want your jobs to run sequentially, why do you run them in the background at all? – KamilCuk Nov 08 '19 at 22:23
  • Here what I want: One function which takes dump of a databases sequentially. And when this function finishes 1 dump, it should execute the restore function (in background) and continue with taking the next dump. The problem I thought of with this approach is, what if the dump process starts too many background restore functions and overwhelms the DB. So I want only 1 restore function to run at any given time. – Amar C Nov 08 '19 at 22:34
  • Och, so you want to run 1 dump function and 1 restore function to run both a the same time, right? And when the first dump function finishes, then the second dump function starts and first restore function starts. Right? – KamilCuk Nov 08 '19 at 22:36
  • .... and when the second dump function finishes, if the first restore is still running it should not start another restore until that restore finishes, it should just continue with the next dump. – Amar C Nov 08 '19 at 22:40
  • 1
    You can make a queue out of files. Dump process produces files, one at a time. The restore proces consumes dumped files, one at a time. – Dzienny Nov 08 '19 at 22:52
  • How to go about doing that? I saw some implementation with FIFO, but I am not entirely sure that is the right way, since fifo is waits for some one to read when you write something. – Amar C Nov 08 '19 at 23:27
  • You can use **GNU Parallel** as a mutex... https://stackoverflow.com/a/37303133/2836621 – Mark Setchell Nov 09 '19 at 07:44

4 Answers4

4

Just pipe it:

seq 1 5 |
while read l; do
    DUMP > "$l".dump
    echo "$l"
done |
while read l; do
    RESTORE < "$l".dump
    echo "$l"
done

But it's probably better to use another descriptor to transfer the data between the pipes, so that logs print nicely:

seq 1 5 |
while read l; do
   DUMP "$l"
   echo "$l" >&3
done 3> >(
    while read l; do
        RESTORE "$l"
    done
) |
cat

Example execution with two stubs:

DUMP() {
    sleep 0.$(($RANDOM % 10))
    echo "DUMPING $1"
}
RESTORE() {
    sleep 0.$(($RANDOM % 10))
    echo "RESTORING $1"
}

looks cool:

DUMPING 1
RESTORING 1
DUMPING 2
RESTORING 2
DUMPING 3
DUMPING 4
DUMPING 5
RESTORING 3
RESTORING 4
RESTORING 5

The | cat on the end is needed to synchronize the process substitution.

What is cool about it, you can use tools like xargs to easily parallelize the DUMP and RESTORE functions, ex run 3 DUMPs in parallel and 2 RESTORE in parallel:

DUMP() {
    echo "DUMPING $1"
    sleep 0.$(($RANDOM % 10))
    echo "DUMPED $1"
}
RESTORE() {
    echo "RESTORING $1"
    sleep 0.$(($RANDOM % 10))
    echo "RESTORED $1"
}

export -f DUMP RESTORE
seq 1 5 |
xargs -n1 -P3 bash -c 'DUMP "$1"; echo "$1" >&3' -- 3> >(
    xargs -n1 -P2 bash -c 'RESTORE "$1"' --
) | cat

And it looks even cooler:

DUMPING 1
DUMPING 2
DUMPING 3
DUMPED 3
RESTORING 3
DUMPING 4
DUMPED 4
RESTORED 3
RESTORING 4
DUMPING 5
DUMPED 1
RESTORING 1
RESTORED 4
DUMPED 2
RESTORING 2
DUMPED 5
RESTORED 2
RESTORING 5
RESTORED 1
RESTORED 5
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
2

I would guess that your script fails because every invocation of RESTORE_CALLER & creates a new process and a fresh current_restore_pid which is not shared.

If you split out the restore procedure into a separate script, you could use flock(1) to set an exclusive lock so that only one restore runs at a time (ordering is not guaranteed):

#!/bin/bash

lockfile=$(mktemp)
for ((i=0;i<5;i++)); do
    dump-function args
    flock -x "$lockfile" restore-program args &
done
rm "$lockfile"
jhnc
  • 11,310
  • 1
  • 9
  • 26
  • I think something like `flock` is needed, I did not succeed it with a local function like `RESTORE()`. Can you add some functions to your solution? – Walter A Nov 09 '19 at 18:18
  • @WalterA Thanks, I hadn't noticed that `tempfile` is a debian-ism. Not sure what you are asking in your comment. – jhnc Nov 11 '19 at 00:52
  • I tried `flock`, but did not get it working with functions. I would like to play with a working prototype, where I can see the restore function waiting for an event (flock releasing the lock) to start running. – Walter A Nov 11 '19 at 09:44
  • @WalterA my `dump-function args` should be replaced by some shell function (or script) invocation. my `restore-program args` should be replaced by some external shell script invocation - not a function stored in the same file (although @KamilCuk's solution illustrates how one might use functions with flock - and his solution performs restores in order which mine may not) – jhnc Nov 11 '19 at 19:13
1

Posting alternate answer, to allow for unlimited parallel dump, while allowing only one concurrent restore at any point.

Solution relatively straight forward: Dump jobs are forked, keeping track of each PID in array. Looping over restore job, and checking for each restore jobs that the corresponding dump jobs has completed, and that the previous restore jobs is also completed.

#! /bin/bash

RESTORE(){
        echo "Start Restore $1 - $$"
        sleep 10
        echo "End Restore $1 - $$"
}

DUMP() {
        echo "Start Dump $1 - $$"
        sleep 15
        echo "End Dump $1 - $$"
}


RUN_ALL(){

for ((i=0;i<5;i++));do
        DUMP $i &
        dump_pid[$i]=$!
done
restore_pid=
for ((i=0;i<5;i++));do
        wait ${dump_pid[i]}
        [ "$restore_pid" ] && wait $restore_pid
        RESTORE $i &
        restore_pid=$!
done

}

RUN_ALL
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • The DUMP's are made in parallel too. I think the OP wants all the DUMP's sequently and all RESTORE's sequently, but the RESTORE's may start as soon as the first DUMP is finished. – Walter A Nov 09 '19 at 18:14
  • Might be better to leverage the 'flock' approach to get those done. It probably more effective than to use bash to synchronize that various dump/restore. – dash-o Nov 09 '19 at 20:25
0

I believe the logic was correct, but the implementation has two places where tasks were sent to the background by mistake:

  • In RESTORE, the '&' on 'sleep ...', make RESTORE return immediately.
  • In DUMP, the '&' on 'RESTORE_CALLER ...', forks the restore into separate sub-shell, preventing wait from working (processes can only wait for direct children).

At the end, there is only one '&' - on the RESTORE operation, which is the only operation to go into the background.


RESTORE(){
# Remove backgroup
# sleep 10 && echo "restore done" &
sleep 10 && echo "restore done"
}


RESTORE_CALLER() {
echo "waiting for any restore with pid ${current_restore_pid}"
wait ${current_restore_pid}
echo "Calling restore"
RESTORE &
current_restore_pid=$!
}


DUMP(){
for ((i=0;i<5;i++));do
echo "dumping "

echo "restore caller"
# Remove Background
# RESTORE_CALLER &
RESTORE_CALLER
done
} 

DUMP 
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • I found the same solution. Problem: The DUMP function pauses when the RESTORE is waiting. You would like to have the 5 DUMP's finished while the RESORE's are still running/queued. – Walter A Nov 09 '19 at 14:01
  • Good point. This will require changes. Probably pre-fork all the DUMP, then make each RESTORE wait for the corresponding dump AND for the previous RESTORE. My understanding is that the DUMP time is very small, so I'm not sure if this represent an issue. – dash-o Nov 09 '19 at 15:29
  • @WalterA I've posted revised answer, for concurrent DUMP, as separate answer. – dash-o Nov 09 '19 at 16:17
  • I upvoted your other answer. IMHO you should delete this one. I am working on an alternative answer. – Walter A Nov 09 '19 at 16:59