diff --git a/sql/binlog.cc b/sql/binlog.cc
index cef949f..b55e541 100644
--- a/sql/binlog.cc
+++ b/sql/binlog.cc
@@ -86,7 +86,7 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
                                                       THD *thd);
 static int binlog_commit(handlerton *hton, THD *thd, bool all);
 static int binlog_rollback(handlerton *hton, THD *thd, bool all);
-static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+int binlog_prepare(handlerton *hton, THD *thd, bool all);
 static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
 static int binlog_clone_consistent_snapshot(handlerton *hton, THD *thd,
                                             THD *from_thd);
@@ -1505,7 +1505,7 @@ inline bool is_loggable_xa_prepare(THD *thd)
                           has_state(XID_STATE::XA_IDLE));
 }
 
-static int binlog_prepare(handlerton *hton, THD *thd, bool all)
+int binlog_prepare(handlerton *hton, THD *thd, bool all)
 {
   DBUG_ENTER("binlog_prepare");
   if (!all)
diff --git a/sql/handler.cc b/sql/handler.cc
index 33d9690..65a1171 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1459,6 +1459,8 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg,
   DBUG_VOID_RETURN;
 }
 
+int binlog_prepare(handlerton *hton, THD *thd, bool all);
+
 /**
   @retval
     0   ok
@@ -1473,6 +1475,8 @@ int ha_prepare(THD *thd)
 
   if (trn_ctx->is_active(Transaction_ctx::SESSION))
   {
+    const bool is_xa_prepare= (thd->lex->sql_command == SQLCOM_XA_PREPARE);
+    handlerton *ht0 = NULL;
     const Ha_trx_info *ha_info= trn_ctx->ha_trx_info(
       Transaction_ctx::SESSION);
     bool gtid_error= false, need_clear_owned_gtid= false;
@@ -1490,6 +1494,25 @@ int ha_prepare(THD *thd)
     while (ha_info)
     {
       handlerton *ht= ha_info->ht();
+      /*
+        Work around mysql bug: Bug #84297  Engine prepare executed after flush
+        stage.
+        Binlog is always the 1st storage engine, in xa prepare execution,
+        binlog of the txn is flushed in binlog_prepare() which is executed first
+        in the loop here, and this is wrong!
+        THe work around is to execute it last, after innodb_xa_prepare() so that
+        the txn is flushed to innodb log *before* it's flushed onto binlog, just
+        as normal non-xa txn commits do.
+      */
+      if (ht0 == NULL && is_xa_prepare && ht->prepare == binlog_prepare)
+      {
+        DBUG_ASSERT(ht->prepare == binlog_prepare);
+        ht0= ht;
+        ha_info= ha_info->next();
+        DBUG_ASSERT(ha_info != NULL);
+        continue;
+      }
+again:
       thd->status_var.ha_prepare_count++;
       if (ht->prepare)
       {
@@ -1510,7 +1533,21 @@ int ha_prepare(THD *thd)
                             ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
                             ha_resolve_storage_engine_name(ht));
       }
+
+      if (!ha_info)
+        break; // binlog_prepare() is just called.
+
       ha_info= ha_info->next();
+      if (!ha_info && ht0)
+      {
+        /*
+          Run binlog_prepare() at last, after all engines have prepared.
+        */
+        DBUG_ASSERT(is_xa_prepare);
+        ht= ht0;
+        ht0= NULL;
+        goto again;
+      }
     }
 
     DBUG_ASSERT(thd->get_transaction()->xid_state()->