public ShareGroupHeartbeatRequestData buildRequestData() {
            ShareGroupHeartbeatRequestData data = new ShareGroupHeartbeatRequestData();

            // GroupId - always sent
            data.setGroupId(shareMembershipManager.groupId());

            // MemberId - always sent, empty until it has been received from the coordinator
            data.setMemberId(shareMembershipManager.memberId());

            // MemberEpoch - always sent
            data.setMemberEpoch(shareMembershipManager.memberEpoch());

            // RackId - only sent the first time, because it does not change
            if (sentFields.rackId == null) {
                data.setRackId(shareMembershipManager.rackId());
                sentFields.rackId = shareMembershipManager.rackId();
            }

            boolean sendAllFields = shareMembershipManager.state() == MemberState.JOINING;

            // SubscribedTopicNames - only sent when joining or if it has changed since the last heartbeat
            TreeSet<String> subscribedTopicNames = new TreeSet<>(this.subscriptions.subscription());
            if (sendAllFields || !subscribedTopicNames.equals(sentFields.subscribedTopicNames)) {
                data.setSubscribedTopicNames(new ArrayList<>(this.subscriptions.subscription()));
                sentFields.subscribedTopicNames = subscribedTopicNames;
            }

            return data;
        }
Builds the heartbeat requests correctly, ensuring that all information is sent according to
the protocol, but subsequent requests do not send information which has not changed. This
is important to ensure that reconciliation completes successfully.
clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareHeartbeatRequestManager.java