c++postgresqllibpqxx

subscribing to two trigger channels using libpqxx


I am trying to subscribe to two different notifications using libpqxx. I found this unit test which I modified to serve my objective.

class TestListener final : public pqxx::notification_receiver
{
  bool m_done;

public:
  explicit TestListener(pqxx::connection &conn, std::string Name) :
          pqxx::notification_receiver(conn, Name), m_done(false)
  {}

  void operator()(std::string const &, int be_pid) override
  {
    m_done = true;
    std::cout << "Received notification: " << channel() << " pid=" << be_pid
              << std::endl;
  }

  bool done() const { return m_done; }
};

I am trying to create a function that simply waits for the notifications and exits as soon as it receives them.


// ...

void waitForChannel(TestListener &L){
    while(!L.done()){};
    std::cout << "received notificaton for channel " << L.channel() << "\n";
};

int main(){
    
    // assume the connection is made
    // ...

    TestListener L1{conn, "channel1"};
    TestListener L2{conn, "channel2"};

    pqxx::perform([&conn, &L1] {
        pqxx::work tx{conn};
        tx.exec0("NOTIFY " + tx.quote_name(L1.channel()));
        tx.commit();
    });

    pqxx::perform([&conn, &L2] {
        pqxx::work tx{conn};
        tx.exec0("NOTIFY " + tx.quote_name(L2.channel()));
        tx.commit();
    });


    while(true){
        waitForChannel(L1);

        waitForChannel(L2);
    }

    return 0;
}

However, I have not been able to receive any signal and gets stuck in while(!L.done()).

Could someone kindly help?


Solution

  • If you wish to listen for database changes, there should be a database trigger set on the table of interest such as

    CREATE OR REPLACE FUNCTION notification_trigger() RETURNS TRIGGER AS
    $$
    BEGIN
        PERFORM pg_notify('<notification_channel>',
                to_json(NEW)::TEXT
        );
        RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE OR REPLACE TRIGGER capture_change_trigger AFTER INSERT OR UPDATE OR DELETE ON <db_table>
    FOR EACH ROW EXECUTE FUNCTION notification_trigger();
    

    And also make sure to await for notifications using the await_notification method.

    TestListener L1{conn, "channel1"};
    conn.await_notification();
    

    If you don't want to block awaiting for notifications, you could use get_notifs.