@@ -2,6 +2,7 @@ package iceberg
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
67 "maps"
78 "regexp"
@@ -31,6 +32,7 @@ type Iceberg struct {
3132 server * serverInstance // Java server instance
3233 schema map [string ]string // schema for current thread associated with Java writer (col -> type)
3334 writer Writer // writer instance
35+ olake2PCState * types.MetadataState // olake_2pc_state for current stream
3436
3537 // Why Schema On Thread Level?
3638 // Schema on thread level is identical to the writer instance available in the Java server.
@@ -72,7 +74,7 @@ func (i *Iceberg) NewWriter(ctx context.Context) (Writer, error) {
7274 return legacywriter .New (i .options , i .schema , i .stream , i .server ), nil
7375}
7476
75- func (i * Iceberg ) Setup (ctx context.Context , stream types.StreamInterface , globalSchema any , options * destination.Options ) (any , error ) {
77+ func (i * Iceberg ) Setup (ctx context.Context , stream types.StreamInterface , globalSchema any , options * destination.Options ) (any , * types. MetadataState , error ) {
7678 i .options = options
7779 i .stream = stream
7880 i .partitionInfo = make ([]internal.PartitionInfo , 0 )
@@ -82,13 +84,13 @@ func (i *Iceberg) Setup(ctx context.Context, stream types.StreamInterface, globa
8284 if partitionRegex != "" {
8385 err := i .parsePartitionRegex (partitionRegex )
8486 if err != nil {
85- return nil , fmt .Errorf ("failed to parse partition regex: %s" , err )
87+ return nil , nil , fmt .Errorf ("failed to parse partition regex: %s" , err )
8688 }
8789 }
8890
8991 server , err := newIcebergClient (i .config , i .partitionInfo , options .ThreadID , false , isUpsertMode (stream , options .Backfill ), i .stream .GetDestinationDatabase (& i .config .IcebergDatabase ))
9092 if err != nil {
91- return nil , fmt .Errorf ("failed to start iceberg server: %s" , err )
93+ return nil , nil , fmt .Errorf ("failed to start iceberg server: %s" , err )
9294 }
9395
9496 // persist server details
@@ -115,20 +117,30 @@ func (i *Iceberg) Setup(ctx context.Context, stream types.StreamInterface, globa
115117
116118 response , err := i .server .SendClientRequest (ctx , & requestPayload )
117119 if err != nil {
118- return nil , fmt .Errorf ("failed to load or create table: %s" , err )
120+ return nil , nil , fmt .Errorf ("failed to load or create table: %s" , err )
119121 }
120122
121123 ingestResponse := response .(* proto.RecordIngestResponse )
122124 schema , err = parseSchema (ingestResponse .GetResult ())
123125 if err != nil {
124- return nil , fmt .Errorf ("failed to parse schema from resp[%s]: %s" , ingestResponse .GetResult (), err )
126+ return nil , nil , fmt .Errorf ("failed to parse schema from resp[%s]: %s" , ingestResponse .GetResult (), err )
127+ }
128+
129+ // Capture optional olake_2pc state from table metadata without returning early,
130+ // so we fall through to create the writer for this thread.
131+ if olake2PCState := ingestResponse .GetOlake_2PcState (); olake2PCState != "" {
132+ var metadataState types.MetadataState
133+ if err := json .Unmarshal ([]byte (olake2PCState ), & metadataState ); err != nil {
134+ return schema , nil , fmt .Errorf ("failed to unmarshal 2pc metadata state: %s" , err )
135+ }
136+ i .olake2PCState = & metadataState
125137 }
126138 } else {
127139 // set global schema for current thread
128140 var ok bool
129141 schema , ok = globalSchema .(map [string ]string )
130142 if ! ok {
131- return nil , fmt .Errorf ("failed to convert globalSchema of type[%T] to map[string]string" , globalSchema )
143+ return nil , nil , fmt .Errorf ("failed to convert globalSchema of type[%T] to map[string]string" , globalSchema )
132144 }
133145 }
134146
@@ -137,19 +149,19 @@ func (i *Iceberg) Setup(ctx context.Context, stream types.StreamInterface, globa
137149
138150 writer , err := i .NewWriter (ctx )
139151 if err != nil {
140- return nil , fmt .Errorf ("failed to create iceberg writer: %v" , err )
152+ return nil , nil , fmt .Errorf ("failed to create iceberg writer: %v" , err )
141153 }
142154 i .writer = writer
143155
144- return schema , nil
156+ return schema , i . olake2PCState , nil
145157}
146158
147159// note: java server parses time from long value which will in milliseconds
148160func (i * Iceberg ) Write (ctx context.Context , records []types.RawRecord ) error {
149161 return i .writer .Write (ctx , records )
150162}
151163
152- func (i * Iceberg ) Close (ctx context.Context ) error {
164+ func (i * Iceberg ) Close (ctx context.Context , finalMetadataState any ) error {
153165 // skip flushing on error
154166 defer func () {
155167 if i .server == nil {
@@ -171,7 +183,7 @@ func (i *Iceberg) Close(ctx context.Context) error {
171183 // skip commit in case of context cancellation
172184 return ctx .Err ()
173185 default :
174- return i .writer .Close (ctx )
186+ return i .writer .Close (ctx , finalMetadataState )
175187 }
176188}
177189
@@ -193,7 +205,7 @@ func (i *Iceberg) Check(ctx context.Context) error {
193205 // to close client properly
194206 i .server = server
195207 defer func () {
196- i .Close (ctx )
208+ i .Close (ctx , nil )
197209 }()
198210
199211 ctx , cancel := context .WithTimeout (ctx , 300 * time .Second )
@@ -504,7 +516,7 @@ func (i *Iceberg) DropStreams(ctx context.Context, dropStreams []types.StreamInt
504516 // to close client properly
505517 i .server = server
506518 defer func () {
507- i .Close (ctx )
519+ i .Close (ctx , nil )
508520 }()
509521
510522 logger .Infof ("Starting Clear Iceberg destination for %d selected streams" , len (dropStreams ))
0 commit comments