Skip to content

model/iceberg_mode: generalize config to key/value/headers sections#30467

Open
wdberkeley wants to merge 1 commit into
devfrom
key-header-translation-p1-config
Open

model/iceberg_mode: generalize config to key/value/headers sections#30467
wdberkeley wants to merge 1 commit into
devfrom
key-header-translation-p1-config

Conversation

@wdberkeley
Copy link
Copy Markdown
Contributor

Replaces the flat 4-variant iceberg_mode (disabled, key_value, value_schema_id_prefix, value_schema_latest) with a structured section-based config supporting independent key, value, and headers options.

New config grammar:

(";"
)* section ::= ("key"|"value"|"headers") ":"

Key/value opts: mode=(binary|schema_id_prefix|schema_latest),
subject=, protobuf_name=
Headers opts: value_type=(binary|string),
on_decode_error=(replace|null|drop)

All old config strings (disabled, key_value, value_schema_id_prefix, value_schema_latest[:subject=S,protobuf_name=N]) remain valid and parse into equivalent enabled_impl objects. Wire format preserves old discriminants 0-3 for configs expressible in the old format; discriminant 4 is added for configs requiring key schema or string headers.

The key and headers sections are fully parsed, validated, and round-tripped but not yet acted on by the translation pipeline. datalake_manager.cc is migrated to the new API and currently reads only the value section's schema mode.

Follow-ups will implement key and header translation.

Release notes come when we are landing the final PR, not this initial one.

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v26.1.x
  • v25.3.x
  • v25.2.x

Release Notes

  • none

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR generalizes model::iceberg_mode from legacy flat variants into a structured key/value/headers configuration while preserving legacy string and wire compatibility where possible.

Changes:

  • Replaces the internal iceberg_mode representation with disabled/enabled section configs.
  • Adds parsing, formatting, serde round-trip behavior, and Boost tests for legacy and new config strings.
  • Migrates datalake and metrics code to use the new value-section API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/v/model/metadata.h Defines the new structured iceberg_mode API and config types.
src/v/model/model.cc Implements parsing, formatting, and wire serialization for legacy and section-based modes.
src/v/model/tests/iceberg_mode_test.cc Adds coverage for parsing, formatting, string round-trips, and wire round-trips.
src/v/model/tests/BUILD Registers the new Boost test target.
src/v/datalake/datalake_manager.cc Switches datalake resolver/translator selection to the value section.
src/v/cluster/metrics_reporter.cc Updates Iceberg metrics classification to use the new API.
docs/iceberg-mode-config-design.md Adds the design document for the generalized Iceberg mode configuration.
Comments suppressed due to low confidence (2)

src/v/model/model.cc:838

  • This compatibility shortcut drops non-default fields that are not part of the legacy encoding, such as headers:on_decode_error=drop with the default value_type=binary (and key/value subject or protobuf_name when their mode remains binary). Those strings parse into enabled_impl values that compare unequal after a wire or string round-trip, despite being accepted by the new grammar. Either reject/normalize options that are not meaningful for the selected mode during parsing, or include all non-default stored fields in the check before using a legacy discriminant.
    if (
      e.key.mode == iceberg_mode::schema_mode::binary
      && e.headers.value_type == iceberg_mode::header_value_type::binary) {

src/v/model/model.cc:916

  • The legacy formatting path is selected based only on key.mode and headers.value_type, so accepted configs with other non-default fields (for example headers:on_decode_error=drop while value_type defaults to binary, or a subject on a binary key/value section) are formatted as a legacy token and lose those fields on parse. Please either reject/normalize those fields when they are not meaningful, or only choose the legacy string when the full stored config is equivalent to that legacy mode.
    if (
      e.key.mode == schema_mode::binary
      && e.headers.value_type == header_value_type::binary) {

Comment thread src/v/model/model.cc
Comment thread src/v/cluster/metrics_reporter.cc
@vbotbuildovich
Copy link
Copy Markdown
Collaborator

vbotbuildovich commented May 13, 2026

Retry command for Build#84416

please wait until all jobs are finished before running the slash command

/ci-repeat 1
skip-redpanda-build
skip-units
skip-rebase
tests/rptest/tests/controller_snapshot_test.py::ControllerSnapshotTest.test_upgrade_compat
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_from_cloud@{"cloud_storage_type":1}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"one_below_high_watermark"}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_and_segment_metadata","cloud_storage_type":2}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":2,"retention_type":"retention.bytes"}
tests/rptest/tests/datalake/topic_properties_test.py::TopicPropertiesTest.test_iceberg_topic_as_read_replica_is_rejected
tests/rptest/tests/cloud_storage_usage_test.py::CloudStorageUsageTest.test_cloud_storage_usage_reporting
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_spillover@{"cloud_storage_type":2}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"random_offset"}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_moving_not_fully_initialized_partition@{"num_to_upgrade":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_existence","cloud_storage_type":2}
tests/rptest/tests/cloud_storage_scrubber_test.py::CloudStorageScrubberTest.test_scrubber@{"cloud_storage_type":1}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_role_with_group_members_restore
tests/rptest/tests/consumer_group_recovery_test.py::ConsumerOffsetsRecoveryTest.test_consumer_offsets_partition_recovery@{"force_offset_upload_failures":false}
tests/rptest/tests/log_compaction_test.py::LogCompactionTxRemovalUpgradeFrom25_2_Test.test_tx_control_batch_removal_with_upgrade
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.drop_lifecycle_marker_test@{"cloud_storage_type":2}
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::TSWithAlreadyCompactedTopic.test_initial_upload
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_time_based_retention@{"cloud_storage_type":2}
tests/rptest/tests/remote_label_test.py::RemoteLabelsTest.test_share_bucket_delete_topic@{"cloud_storage_type":2}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_invalid_destination@{"num_to_upgrade":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_prevent_recovery@{"cloud_storage_type":2}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":1,"retention_type":"retention.bytes"}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"lz4"}
tests/rptest/tests/audit_log_test.py::AuditLogUpgradeTest.test_audit_log_upgrade_all_nodes
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_bootstrap_with_recovery
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"rest_hadoop","cloud_storage_type":1}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_overlapping_changes@{"num_to_upgrade":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndThrottlingTest.test_throttling@{"cloud_storage_type":1}
tests/rptest/tests/upgrade_test.py::UpgradeBackToBackTest.test_upgrade_with_all_workloads@{"single_upgrade":true}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention@{"cloud_storage_type":1}
tests/rptest/tests/topic_creation_test.py::CreateTopicsTest.test_no_log_bloat_when_recreating_existing_topics
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_and_segment_metadata","cloud_storage_type":1}
tests/rptest/tests/alter_topic_configuration_test.py::ShadowIndexingGlobalConfig.test_topic_manifest_reupload
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"nessie","cloud_storage_type":1}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":2,"retention_type":"retention.ms"}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_basic_controller_snapshot_restore
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_dynamic@{"num_to_upgrade":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_spillover@{"cloud_storage_type":1}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"start_offset"}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention@{"cloud_storage_type":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"no_check","cloud_storage_type":2}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention_application@{"cloud_storage_type":2}
tests/rptest/tests/read_replica_e2e_test.py::ReadReplicasUpgradeTest.test_upgrades@{"cloud_storage_type":1}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":1,"retention_type":"retention.ms"}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_group_acl_restore
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"snappy"}
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"rest_jdbc","cloud_storage_type":1}
tests/rptest/tests/partition_movement_upgrade_test.py::PartitionMovementUpgradeTest.test_basic_upgrade
tests/rptest/tests/e2e_shadow_indexing_test.py::ShadowIndexingInfiniteRetentionTest.test_segments_not_deleted@{"cloud_storage_type":2}
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::ShadowIndexingCompactedTopicTest.test_upload@{"cloud_storage_type":2}
tests/rptest/tests/upgrade_test.py::UpgradeFromPriorFeatureVersionCloudStorageTest.test_rolling_upgrade@{"cloud_storage_type":1}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_empty@{"num_to_upgrade":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_existence","cloud_storage_type":1}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention_application@{"cloud_storage_type":1}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"zstd"}
tests/rptest/tests/e2e_shadow_indexing_test.py::ShadowIndexingInfiniteRetentionTest.test_segments_not_deleted@{"cloud_storage_type":1}
tests/rptest/tests/cloud_storage_scrubber_test.py::CloudStorageScrubberTest.test_scrubber@{"cloud_storage_type":2}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_mixed_principal_acl_restore
tests/rptest/tests/nodes_decommissioning_test.py::NodesDecommissioningTest.test_decommissioning_and_upgrade
tests/rptest/tests/data_transforms_test.py::DataTransformsRpcUpgradeTest.test_upgrade_one_node
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::ShadowIndexingCompactedTopicTest.test_upload@{"cloud_storage_type":1}
tests/rptest/tests/timequery_test.py::TimeQueryTest.test_timequery@{"batch_cache":false,"cloud_storage":true,"spillover":true}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"no_check","cloud_storage_type":1}
tests/rptest/transactions/consumer_offsets_test.py::VerifyConsumerOffsetsThruUpgrades.test_consumer_group_offsets@{"versions_to_upgrade":1}
tests/rptest/tests/cloud_retention_test.py::CloudRetentionTest.test_gc_entire_manifest@{"cloud_storage_type":1}
tests/rptest/tests/datalake/datalake_upgrade_test.py::DatalakeUpgradeTest.test_upload_through_upgrade@{"cloud_storage_type":1,"migration_type":"recreate_table","query_engine":"spark"}
tests/rptest/tests/cloud_storage_timing_stress_test.py::CloudStorageTimingStressTest.test_cloud_storage_with_partition_moves@{"cleanup_policy":"delete"}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.topic_delete_cloud_storage_test@{"cloud_storage_type":2,"disable_delete":false}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore@{"cloud_storage_type":1,"message_size":5000,"num_messages":100000,"recovery_overrides":{}}
tests/rptest/tests/partition_movement_test.py::SIPartitionMovementTest.test_shadow_indexing@{"cloud_storage_type":2,"num_to_upgrade":2,"with_cloud_topics":false}
tests/rptest/tests/tiered_storage_pause_test.py::TestTieredStoragePause.test_safe_pause_resume@{"allow_gaps_cluster_level":false,"allow_gaps_topic_level":false}
tests/rptest/tests/cloud_retention_test.py::CloudRetentionTest.test_cloud_retention@{"cloud_storage_type":1,"max_consume_rate_mb":20}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore_with_aborted_tx@{"cloud_storage_type":1,"recovery_overrides":{"redpanda.remote.read":true,"redpanda.remote.write":true,"retention.local.target.bytes":1024}}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTestCompactedTopic.test_compacting_during_leadership_transfer@{"cloud_storage_type":1}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.topic_delete_unavailable_test@{"cloud_storage_type":1}
tests/rptest/tests/raft_recovery_test.py::RaftRecoveryUpgradeTest.test_upgrade
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_topic_recovery_retention_settings
tests/rptest/tests/random_node_operations_smoke_test.py::RedpandaNodeOperationsSmokeTest.test_node_ops_smoke_test@{"cloud_storage_type":1,"mixed_versions":false}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_move_consumer_offsets_intranode@{"num_to_upgrade":2}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore_with_config_batches@{"cloud_storage_type":1,"num_messages":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset@{"cloud_storage_type":1}
tests/rptest/tests/log_compaction_test.py::LogCompactionTxRemovalUpgradeFrom25_3_1_Test.test_tx_control_batch_removal_with_upgrade
tests/rptest/tests/upgrade_test.py::UpgradeWithWorkloadTest.test_rolling_upgrade_with_rollback@{"upgrade_after_rollback":false}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"at_high_watermark"}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_vcluster_id@{"cloud_storage_type":2}
tests/rptest/tests/random_node_operations_smoke_test.py::RedpandaNodeOperationsSmokeTest.test_node_ops_smoke_test@{"cloud_storage_type":1,"mixed_versions":true}
tests/rptest/tests/log_compaction_test.py::LogCompactionTxRemovalUpgradeFrom25_3_1_Test.test_tx_control_batch_removal_with_upgrade_and_recovery
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_from_cloud@{"cloud_storage_type":2}
tests/rptest/tests/upgrade_test.py::UpgradeWithWorkloadTest.test_rolling_upgrade_with_rollback@{"upgrade_after_rollback":true}
tests/rptest/tests/cluster_config_test.py::ClusterConfigAzureSharedKey.test_live_shared_key_change@{"cloud_storage_type":2}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"at_segment_boundary"}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_vcluster_id@{"cloud_storage_type":1}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_deletion_stops_move@{"num_to_upgrade":2}

@wdberkeley wdberkeley force-pushed the key-header-translation-p1-config branch from 3d92530 to f8deed0 Compare May 15, 2026 15:52
Replaces the flat 4-variant iceberg_mode (disabled, key_value,
value_schema_id_prefix, value_schema_latest) with a structured
section-based config supporting independent key, value, and headers
options.

New config grammar:
  <section> (";" <section>)*
  section  ::= ("key"|"value"|"headers") ":" <opts>

Key/value opts: mode=(binary|schema_id_prefix|schema_latest),
  subject=<str>, protobuf_name=<str>
Headers opts: value_type=(binary|string),
  on_decode_error=(replace|null|drop)

All old config strings (disabled, key_value, value_schema_id_prefix,
value_schema_latest[:subject=S,protobuf_name=N]) remain valid and parse
into equivalent enabled_impl objects. Wire format preserves old
discriminants 0-3 for configs expressible in the old format; discriminant
4 is added for configs requiring key schema or string headers.

The key and headers sections are fully parsed, validated, and
round-tripped but not yet acted on by the translation pipeline — that
is Rocks 2 and 3. datalake_manager.cc is migrated to the new API and
currently reads only the value section's schema mode.
@wdberkeley wdberkeley force-pushed the key-header-translation-p1-config branch from f8deed0 to aac388c Compare May 15, 2026 15:52
@vbotbuildovich
Copy link
Copy Markdown
Collaborator

vbotbuildovich commented May 15, 2026

Retry command for Build#84517

please wait until all jobs are finished before running the slash command

/ci-repeat 1
skip-redpanda-build
skip-units
skip-rebase
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention_application@{"cloud_storage_type":1}
tests/rptest/tests/e2e_shadow_indexing_test.py::ShadowIndexingInfiniteRetentionTest.test_segments_not_deleted@{"cloud_storage_type":1}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"zstd"}
tests/rptest/tests/cloud_storage_scrubber_test.py::CloudStorageScrubberTest.test_scrubber@{"cloud_storage_type":2}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_mixed_principal_acl_restore
tests/rptest/tests/data_transforms_test.py::DataTransformsRpcUpgradeTest.test_upgrade_one_node
tests/rptest/tests/nodes_decommissioning_test.py::NodesDecommissioningTest.test_decommissioning_and_upgrade
tests/rptest/tests/upgrade_test.py::UpgradeWithWorkloadTest.test_rolling_upgrade_with_rollback@{"upgrade_after_rollback":true}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_vcluster_id@{"cloud_storage_type":1}
tests/rptest/tests/controller_snapshot_test.py::ControllerSnapshotTest.test_upgrade_compat
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_from_cloud@{"cloud_storage_type":1}
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::ShadowIndexingCompactedTopicTest.test_upload@{"cloud_storage_type":2}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"one_below_high_watermark"}
tests/rptest/tests/upgrade_test.py::UpgradeFromPriorFeatureVersionCloudStorageTest.test_rolling_upgrade@{"cloud_storage_type":1}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_existence","cloud_storage_type":1}
tests/rptest/tests/alter_topic_configuration_test.py::ShadowIndexingGlobalConfig.test_topic_manifest_reupload
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"nessie","cloud_storage_type":1}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":2,"retention_type":"retention.ms"}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_basic_controller_snapshot_restore
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_dynamic@{"num_to_upgrade":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_spillover@{"cloud_storage_type":1}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.drop_lifecycle_marker_test@{"cloud_storage_type":2}
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::TSWithAlreadyCompactedTopic.test_initial_upload
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"start_offset"}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention@{"cloud_storage_type":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_prevent_recovery@{"cloud_storage_type":2}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":1,"retention_type":"retention.bytes"}
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"rest_hadoop","cloud_storage_type":1}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_bootstrap_with_recovery
tests/rptest/tests/audit_log_test.py::AuditLogUpgradeTest.test_audit_log_upgrade_all_nodes
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_overlapping_changes@{"num_to_upgrade":2}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.drop_lifecycle_marker_test@{"cloud_storage_type":1}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndThrottlingTest.test_throttling@{"cloud_storage_type":1}
tests/rptest/tests/upgrade_test.py::UpgradeWithWorkloadTest.test_rolling_upgrade
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention@{"cloud_storage_type":1}
tests/rptest/tests/schema_registry_test.py::SchemaRegistryTransportCompatTest.test_upgrade_kafka_to_rpc
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_prevent_recovery@{"cloud_storage_type":1}
tests/rptest/tests/cloud_storage_scrubber_test.py::CloudStorageScrubberTest.test_scrubber@{"cloud_storage_type":1}
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_role_with_group_members_restore
tests/rptest/tests/consumer_group_recovery_test.py::ConsumerOffsetsRecoveryTest.test_consumer_offsets_partition_recovery@{"force_offset_upload_failures":false}
tests/rptest/tests/log_compaction_test.py::LogCompactionTxRemovalUpgradeFrom25_2_Test.test_tx_control_batch_removal_with_upgrade
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_time_based_retention@{"cloud_storage_type":2}
tests/rptest/tests/remote_label_test.py::RemoteLabelsTest.test_share_bucket_delete_topic@{"cloud_storage_type":2}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_invalid_destination@{"num_to_upgrade":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"check_manifest_and_segment_metadata","cloud_storage_type":2}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"lz4"}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore_with_aborted_tx@{"cloud_storage_type":2,"recovery_overrides":{}}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":2,"retention_type":"retention.bytes"}
tests/rptest/tests/datalake/topic_properties_test.py::TopicPropertiesTest.test_iceberg_topic_as_read_replica_is_rejected
tests/rptest/tests/cloud_storage_usage_test.py::CloudStorageUsageTest.test_cloud_storage_usage_reporting
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset_spillover@{"cloud_storage_type":2}
tests/rptest/tests/shadow_indexing_compacted_topic_test.py::ShadowIndexingCompactedTopicTest.test_upload@{"cloud_storage_type":1}
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"random_offset"}
tests/rptest/tests/timequery_test.py::TimeQueryTest.test_timequery@{"batch_cache":false,"cloud_storage":true,"spillover":true}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_moving_not_fully_initialized_partition@{"num_to_upgrade":2}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"no_check","cloud_storage_type":1}
tests/rptest/transactions/consumer_offsets_test.py::VerifyConsumerOffsetsThruUpgrades.test_consumer_group_offsets@{"versions_to_upgrade":1}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_cloud_size_based_retention_application@{"cloud_storage_type":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::ShadowIndexingInfiniteRetentionTest.test_segments_not_deleted@{"cloud_storage_type":2}
tests/rptest/tests/read_replica_e2e_test.py::ReadReplicasUpgradeTest.test_upgrades@{"cloud_storage_type":1}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"snappy"}
tests/rptest/tests/archive_retention_test.py::CloudArchiveRetentionTest.test_delete@{"cloud_storage_type":1,"retention_type":"retention.ms"}
tests/rptest/tests/datalake/cluster_restore_test.py::DatalakeClusterRestoreTest.test_basic@{"catalog_type":"rest_jdbc","cloud_storage_type":1}
tests/rptest/tests/partition_movement_upgrade_test.py::PartitionMovementUpgradeTest.test_basic_upgrade
tests/rptest/tests/cluster_recovery_test.py::ClusterRecoveryTest.test_group_acl_restore
tests/rptest/tests/upgrade_test.py::UpgradeWithWorkloadTest.test_rolling_upgrade_with_rollback@{"upgrade_after_rollback":false}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_vcluster_id@{"cloud_storage_type":2}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_empty@{"num_to_upgrade":2}
tests/rptest/tests/cloud_retention_test.py::CloudRetentionTest.test_cloud_retention@{"cloud_storage_type":2,"max_consume_rate_mb":20}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore_with_aborted_tx@{"cloud_storage_type":2,"recovery_overrides":{"redpanda.remote.read":true,"redpanda.remote.write":true,"retention.local.target.bytes":1024}}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTestCompactedTopic.test_compacting_during_leadership_transfer@{"cloud_storage_type":2}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.partition_movement_test@{"cloud_storage_type":1}
tests/rptest/tests/tiered_storage_pause_test.py::TestTieredStoragePause.test_resume@{"allow_gaps_cluster_level":true,"allow_gaps_topic_level":null}
tests/rptest/tests/retention_policy_test.py::ShadowIndexingCloudRetentionTest.test_topic_recovery_retention_settings
tests/rptest/tests/random_node_operations_smoke_test.py::RedpandaNodeOperationsSmokeTest.test_node_ops_smoke_test@{"cloud_storage_type":1,"mixed_versions":false}
tests/rptest/tests/partition_movement_test.py::PartitionMovementTest.test_move_consumer_offsets_intranode@{"num_to_upgrade":2}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore_with_config_batches@{"cloud_storage_type":1,"num_messages":2}
tests/rptest/tests/e2e_shadow_indexing_test.py::EndToEndShadowIndexingTest.test_reset@{"cloud_storage_type":1}
tests/rptest/tests/log_compaction_test.py::LogCompactionTxRemovalUpgradeFrom25_3_1_Test.test_tx_control_batch_removal_with_upgrade
tests/rptest/tests/delete_records_test.py::DeleteRecordsTest.test_delete_records_segment_deletion@{"cloud_storage_enabled":true,"truncate_point":"at_high_watermark"}
tests/rptest/tests/topic_recovery_test.py::TopicRecoveryTest.test_many_partitions@{"check_mode":"no_check","cloud_storage_type":2}
tests/rptest/tests/cloud_retention_test.py::CloudRetentionTimelyGCTest.test_retention_with_node_failures@{"cloud_storage_type":1}
tests/rptest/tests/compatibility/java_compression_test.py::JavaCompressionTest.test_upgrade_java_compression@{"compression_type":"gzip"}
tests/rptest/tests/cloud_storage_usage_test.py::CloudStorageUsageTest.test_cloud_storage_usage_reporting_with_partition_moves
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.topic_delete_unavailable_test@{"cloud_storage_type":1}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore@{"cloud_storage_type":1,"message_size":5000,"num_messages":100000,"recovery_overrides":{"retention.local.target.bytes":1024}}
tests/rptest/tests/partition_movement_test.py::SIPartitionMovementTest.test_shadow_indexing@{"cloud_storage_type":1,"num_to_upgrade":2,"with_cloud_topics":false}
tests/rptest/tests/partition_movement_test.py::SIPartitionMovementTest.test_cross_shard@{"cloud_storage_type":2,"num_to_upgrade":2,"with_cloud_topics":false}
tests/rptest/tests/cloud_retention_test.py::CloudRetentionTest.test_gc_entire_manifest@{"cloud_storage_type":1}
tests/rptest/tests/cloud_storage_timing_stress_test.py::CloudStorageTimingStressTest.test_cloud_storage_with_partition_moves@{"cleanup_policy":"delete"}
tests/rptest/tests/datalake/datalake_upgrade_test.py::DatalakeUpgradeTest.test_upload_through_upgrade@{"cloud_storage_type":1,"migration_type":"recreate_table","query_engine":"spark"}
tests/rptest/tests/e2e_topic_recovery_test.py::EndToEndTopicRecovery.test_restore@{"cloud_storage_type":1,"message_size":5000,"num_messages":100000,"recovery_overrides":{}}
tests/rptest/tests/partition_movement_test.py::SIPartitionMovementTest.test_shadow_indexing@{"cloud_storage_type":2,"num_to_upgrade":2,"with_cloud_topics":false}
tests/rptest/tests/topic_delete_test.py::TopicDeleteCloudStorageTest.topic_delete_installed_snapshots_test
tests/rptest/tests/tiered_storage_pause_test.py::TestTieredStoragePause.test_safe_pause_resume@{"allow_gaps_cluster_level":false,"allow_gaps_topic_level":true}

Comment thread src/v/model/metadata.h
Comment on lines +684 to +690
/// Action to take when a header value cannot be decoded as UTF-8.
/// Only meaningful when header_value_type == string.
enum class header_on_decode_error : uint8_t {
replace, ///< Replace invalid byte sequences with U+FFFD.
null, ///< Set the header value to null.
drop, ///< Drop the entire header entry.
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if the header_on_decode_error is a hard requirement. It's a bit hard to articulate why I'd ever want to choose anything but replace. Was it a customer ask?

If not, maybe we just do replace by default and omit on_decode_error as a section, and in the future add whatever else we might want?

Comment thread src/v/model/metadata.h
Comment on lines +731 to 733
static iceberg_mode disabled;
static iceberg_mode key_value;
static iceberg_mode value_schema_id_prefix;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Comment thread src/v/model/model.cc
constexpr uint8_t wire_value_schema_latest = 3;
constexpr uint8_t wire_key_and_value_schema = 4;

static ss::logger iceberg_mode_log("iceberg_mode");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: kind of an odd choice, relative to the rest of the codebase

Comment thread src/v/model/model.cc
Comment on lines +731 to +739
if (v == "binary") {
cfg.mode = iceberg_mode::schema_mode::binary;
} else if (v == "schema_id_prefix") {
cfg.mode = iceberg_mode::schema_mode::schema_id_prefix;
} else if (v == "schema_latest") {
cfg.mode = iceberg_mode::schema_mode::schema_latest;
} else {
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a string_switch?

Comment thread src/v/model/model.cc
Comment on lines +707 to +708
// Bit flags tracking seen known sections: bit 0=key, 1=value, 2=headers.
uint8_t seen_known = 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it important to use bit flags here, vs storing the set of seen sections?

Comment thread src/v/model/model.cc
Comment on lines +828 to +829
cfg.subject = {};
cfg.protobuf_name = {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and for the header on_decode_error, WDYT about making it an error instead? By being permissive I'm a little worried that a bad config could sneak in and surprise is one day

Comment thread src/v/model/tests/BUILD
],
)

redpanda_cc_btest(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: new tests should prefer gtest, unless there's something specific in boost you are trying to leverage?

BOOST_CHECK_EQUAL(*m, model::iceberg_mode::disabled);
}

// --- legacy strings ---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd also be good to make sure the ducktape upgrade tests that use Iceberg are tested with all the legacy strings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants