Hi @denesb ,
I found that view_updates::update_entry(const partition_key& base_key, const clustering_or_static_row& update, const clustering_or_static_row& existing, gc_clock::time_point now) was called in function view_updates::generate_update.  There is a line of statement auto diff = update.cells().difference(*_base, kind, existing.cells()); in generate_update.
I found through reproducing problem Major bug: Repair will cause index table data loss! that the diff (actually a row type) here is empty, which results in the cells of the generated GSI being empty.
If the data modified from the base table(update) is the same as the data existing in the node(existing), then diff is empty, resulting in the record only having a key and no cells in GSI.
Why use update_entry, can’t we just use create_entry directly?
void view_updates::update_entry(const partition_key& base_key, const clustering_or_static_row& update, const clustering_or_static_row& existing, gc_clock::time_point now) {
    // While we know update and existing correspond to the same view entry,
    // they may not match the view filter.
    if (!matches_view_filter(*_base, _view_info, base_key, existing, now)) {
        create_entry(base_key, update, now);
        return;
    }
    if (!matches_view_filter(*_base, _view_info, base_key, update, now)) {
        do_delete_old_entry(base_key, existing, update, now);
        return;
    }
    if (can_skip_view_updates(update, existing)) {
        return;
    }
    auto view_rows = get_view_rows(base_key, update, std::nullopt);
    auto update_marker = compute_row_marker(update);
    const auto kind = update.column_kind();
    for (const auto& [r, action] : view_rows) { // r deletablerow
        if (auto rm = std::get_if<row_marker>(&action)) {
            r->apply(*rm);
            vlogger.info("view_updates::update_entry bbb {}", row::printer(*_base, kind, r->cells()));
        } else {
            r->apply(update_marker);
        }
        r->apply(update.tomb());
        vlogger.info("view_updates::update_entry update {}", row::printer(*_base, kind, update.cells()));
        vlogger.info("view_updates::update_entry existing {}", row::printer(*_base, kind, existing.cells()));
   
        auto diff = update.cells().difference(*_base, kind, existing.cells()); // row
        vlogger.info("view_updates::update_entry diif {}", row::printer(*_base, kind, diff)); // diff is none
        add_cells_to_view(*_base, *_view, kind, std::move(diff), r->cells());
        vlogger.info("view_updates::update_entry r {}", row::printer(*_base, kind, r->cells())); // r is none
    }
    _op_count += view_rows.size();
}