vmware / differential-datalog

DDlog is a programming language for incremental computation. It is well suited for writing programs that continuously update their output in response to input changes. A DDlog programmer does not write incremental algorithms; instead they specify the desired input-output mapping in a declarative manner.
MIT License
1.35k stars 117 forks source link

Stopped instance should return a meaningful error message [was: Error when trying to delete a relation]. #1154

Open tatiana-s opened 2 years ago

tatiana-s commented 2 years ago

I'm using the Rust API to interact with a DDlog program and I keep getting the following error when I try to delete any relation at runtime (using just hddlog.apply_updates(&mut delete_updates.into_iter()).unwrap(); where delete_updates has updates of type Update::DeleteValue):

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "failed to communicate with timely dataflow thread 0"'

I am certain that the relation I want to delete is inserted beforehand (and I also don't have any problems with the initial insertions). Any thoughts as to what issues might be causing the failed to communicate with timely dataflow thread 0 error (which seems very unspecific), or how I could go about debugging to find the issue given I don't know much about timely dataflow?

ryzhyk commented 2 years ago

This error means that the worker thread has crashed, so it's likely a bug. Do you see anything else on the stderr from the worker thread by any chance?

Is there a way I could reproduce the error?

tatiana-s commented 2 years ago

I can't seem to see any other information with regards to the error. The error should be reproducible with the following main.rs (basically just an adaptation of the Rust API example):

use differential_datalog::api::HDDlog;
use differential_datalog::ddval::{DDValConvert, DDValue};
use differential_datalog::program::{RelId, Update};
use differential_datalog::{DDlog, DDlogDynamic, DeltaMap};
use type_checker1_ddlog::typedefs::*;
use type_checker1_ddlog::Relations;

fn main() {
    let (hddlog, _) = type_checker1_ddlog::run(1, false).unwrap();
    // Insertion transaction:
    hddlog.transaction_start().unwrap();
    let updates = vec![
        Update::Insert {
            relid: Relations::Main as RelId,
            v: Main { id: 0, body_id: 1 }.into_ddvalue(),
        },
        Update::Insert {
            relid: Relations::Unit as RelId,
            v: Unit { id: 1 }.into_ddvalue(),
        },
    ];
    hddlog.apply_updates(&mut updates.into_iter()).unwrap();
    let mut delta = hddlog.transaction_commit_dump_changes().unwrap();
    println!("State after insertion transaction:");
    dump_delta(&delta);
    hddlog.stop().unwrap();
    // Deletion transaction:
    hddlog.transaction_start().unwrap();
    let delete_updates = vec![Update::DeleteValue {
        relid: Relations::Main as RelId,
        v: Main { id: 0, body_id: 1 }.into_ddvalue(),
    }];
    hddlog
        .apply_updates(&mut delete_updates.into_iter())
        .unwrap();
    delta = hddlog.transaction_commit_dump_changes().unwrap();
    println!("State after deletion transaction:");
    dump_delta(&delta);
    hddlog.stop().unwrap();
}

fn dump_delta(delta: &DeltaMap<DDValue>) {
    for (_, changes) in delta.iter() {
        for (val, weight) in changes.iter() {
            println!("{} {:+}", val, weight);
        }
    }
}

With the following type_checker1.dl program:

typedef Type = UnitType | OkType
typedef ID = signed<32>

input relation Main(id: ID, body_id: ID)
input relation App(id: ID, e1_id: ID, e2_id: ID)
input relation Unit(id: ID)

output relation TypeMain(id: ID, t: Type)
output relation TypeApp(id: ID, t: Type)
output relation TypeUnit(id: ID, t: Type)
output relation Typed(id: ID, t: Type)
output relation OkPrograms(id: ID)

OkPrograms(id) :-
    TypeMain(id, OkType).
TypeMain(id, OkType) :-
    Main(id, body),
    Typed(body, UnitType).
Typed(id, t) :- TypeApp(id, t).
Typed(id, t) :- TypeUnit(id, t).
TypeApp(id, t) :-
    App(id, e1, e2),
    Typed(e1, t),
    Typed(e2, t).
TypeUnit(id, UnitType) :-
    Unit(id).
ryzhyk commented 2 years ago

I suspect, the problem is caused by this line, which kills the DDlog instance after the first transaction:

hddlog.stop().unwrap();

This is not something you want to do in the middle of a session.

tatiana-s commented 2 years ago

Oh I see - I assumed this would end the transaction, not the kill the whole instance, but this is indeed the problem. Thank you!

ryzhyk commented 2 years ago

I don't remember why stop doesn't consume the HDDlog handle, but in any case we should handle this situation more cleanly; at least return a more meaningful error message.