Добавляешь в базу данных триггеры на delete, update и insert, для каждой таблицы (код создания можно сгенерировать по списку таблиц простым скриптом), которые будут заполнять дополнительно созданные тобой таблицы (дублирующую оригинальную структуру или только там что тебе нужно переносить), в которых будут обновленные данные и поля со временем события и его типом. Время тут будет идентификатором (можешь сделать с миллисекундами, параноики могут использовать автоинкрементируемое поле, только сделать его общим на все таблицы, что не выгодно) и единственным индексируемым полем
p.s. для delete можно отдельную таблицу пилить, что бы не тягать лишние колонки пустые (на сколько я помню во многих базах null может занимать заметно места на диске сравнимо с данными)
Затем пишешь простенькое приложение, которое будет считывать эти таблицы (фильтруя на время запуска этого скрипта или по текущему максимальному значению autoincrement, что бы не затрагивать те данные что появились в процессе передачи), отсылать один в один запросы на целевую базу (в т.ч. в том же порядке!) и чистить из этих лог таблиц записи, которые успешно были переданы (ошибки аккуратно отслеживать, например в удаленную базу записали а из локальной не удалили).
Этот скрипт может крутиться в бесконечном цикле, мониторя наличие изменений в базе (например постим инвент из тригера, а на том же php мониторим этот ивент с помощью ibase_wait_event, само собой это можно делать из других языков программирования).
В похожей ситуации весь код был где то строк 100, еще столько же код генерации sql для создания триггеров (ну может больше, если информация о структуре будет не вручную задана).
Задержка переноса данных в этом случае будет равна времени их передачи.. без изменения кода самого приложения, это не изменить