sijms / go-ora

Pure go oracle client
MIT License
796 stars 177 forks source link

Interrupt a running operation #508

Closed rheilek closed 7 months ago

rheilek commented 8 months ago

Using context.WithCancel a call to the cancel func should interrupt the running operation (code snippet below).

ctx, cancel := context.WithCancel(context.Background())

go func () {
    fmt.Println(db.ExecContext(ctx, "begin for i in 1..120 loop dbms_lock.sleep(1); end loop; end;"))
}()

go func() {
    time.Sleep(1 * time.Second)
    cancel()
}()

To get it work i disable Urgent-Data-Transport (OOB) because sendOOB on windows is empty. Furthermore oracle-jdbc-driver and nodejs-thin-driver in my debug cases only (can) send marker packets.

The main challenge is the race conidtion between the running operation (read) and another concurrent read from BreakConnection. That's why i simplify the code with returning an error instead of reading the ORA-01013 packet.

sijms commented 8 months ago

first thank you for your help also the option of cancelling OOB but there are 2 point here 1- in connection break function: you discard returned data from the server and return connection break error but this data is important and contain cursor ID required to close the cursor otherwise cursor leak issue will come back 2- you make a go routine for connect context what about exec and query?

rheilek commented 8 months ago

2 - good point, i moved the code to session.StartContext and returning the channel to close with defer

1 - returning an error on BreakConnection was wrong, now only write the packet because the running session.read already read the packet and will handle the ORA-01013.

pck, err := session.readPacket() // hangs and continue after BreakConnection writeMarkerPacket
if err != nil {
    if e, ok := err.(net.Error); ok && e.Timeout() {
        var breakErr error
        tracer.Print("Read Timeout")
        breakErr = session.BreakConnection(true)
        if breakErr != nil {
            //return nil, err
            tracer.Print("Connection Break With Error: ", breakErr)
            return nil, err
        }
    } else {
        return nil, err
    }
}

if session.IsBreak() {
    for pck == nil {
        pck, err = session.readPacket() // reads the response of marker packet
sijms commented 8 months ago

your model is good but I need to use it for all context type (cancel and timeout) timeout is an old problem and passes with many issue until reach this code model so changing the code need more testing

sijms commented 7 months ago

I use your code make some modification and testing. now available in v 2.8.8