Skip to content

Commit 433f709

Browse files
committed
FIx serialization issue
1 parent 6c0f540 commit 433f709

1 file changed

Lines changed: 31 additions & 24 deletions

File tree

scio-parquet/src/main/scala/com/spotify/scio/parquet/avro/ParquetAvroIO.scala

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import com.spotify.scio.parquet.avro.ParquetAvroIO.WriteParam._
3131
import com.spotify.scio.parquet.read.ParquetReadConfiguration
3232
import com.spotify.scio.parquet.{GcsConnectorUtil, ParquetConfiguration}
3333
import com.spotify.scio.testing.TestDataManager
34+
import com.spotify.scio.transforms._
35+
import com.spotify.scio.transforms.DoFnWithResource.ResourceType
3436
import com.spotify.scio.util.{FilenamePolicySupplier, ScioUtil}
3537
import com.spotify.scio.values.SCollection
3638
import org.apache.avro.Schema
@@ -55,6 +57,7 @@ import scala.jdk.CollectionConverters._
5557
import scala.reflect.ClassTag
5658

5759
sealed trait ParquetAvroIO[T <: IndexedRecord] extends ScioIO[T] {
60+
import ParquetAvroIO._
5861

5962
override type ReadP = ParquetAvroIO.ReadParam[T]
6063
override type WriteP = ParquetAvroIO.WriteParam[T]
@@ -141,36 +144,29 @@ sealed trait ParquetAvroIO[T <: IndexedRecord] extends ScioIO[T] {
141144
override protected def readTest(sc: ScioContext, params: ReadP): SCollection[T] = {
142145
val datumFactory = Option(params.datumFactory).getOrElse(defaultDatumFactory)
143146
implicit val coder: Coder[T] = avroCoder(datumFactory, schema)
144-
// SpecificData.getForClass is only available for 1.9+
145-
val recordClass = datumFactory.getType
146-
val data = if (classOf[SpecificRecordBase].isAssignableFrom(recordClass)) {
147-
val classModelField = recordClass.getDeclaredField("MODEL$")
148-
classModelField.setAccessible(true)
149-
classModelField.get(null).asInstanceOf[SpecificData]
150-
} else {
151-
SpecificData.get()
152-
}
147+
153148
// The projection function is not part of the test input, so it must be applied directly
154149
val projectedFields = Option(params.projection).map(_.getFields.asScala.map(_.name()).toSet)
155150
TestDataManager
156151
.getInput(sc.testId.get)(this)
157152
.toSCollection(sc)
158-
.map { record =>
159-
projectedFields match {
160-
case None => record
161-
case Some(projection) =>
162-
// beam forbids mutations. Create a new record
163-
val copy = data.deepCopy(record.getSchema, record)
164-
record.getSchema.getFields.asScala
165-
.foldLeft(copy) { (c, f) =>
166-
val names = Set(f.name()) ++ f.aliases().asScala.toSet
167-
if (projection.intersect(names).isEmpty) {
168-
// field is not part of the projection. user default value
169-
c.put(f.pos(), data.getDefaultValue(f))
153+
.mapWithResource(dataForClass(datumFactory.getType), ResourceType.PER_INSTANCE) {
154+
case (data, record) =>
155+
projectedFields match {
156+
case None => record
157+
case Some(projection) =>
158+
// beam forbids mutations. Create a new record
159+
val copy = data.deepCopy(record.getSchema, record)
160+
record.getSchema.getFields.asScala
161+
.foldLeft(copy) { (c, f) =>
162+
val names = Set(f.name()) ++ f.aliases().asScala.toSet
163+
if (projection.intersect(names).isEmpty) {
164+
// field is not part of the projection. user default value
165+
c.put(f.pos(), data.getDefaultValue(f))
166+
}
167+
c
170168
}
171-
c
172-
}
173-
}
169+
}
174170
}
175171
}
176172

@@ -235,6 +231,17 @@ sealed trait ParquetAvroIO[T <: IndexedRecord] extends ScioIO[T] {
235231

236232
object ParquetAvroIO {
237233

234+
// SpecificData.getForClass is only available for 1.9+
235+
private def dataForClass[T](recordClass: Class[T]) = {
236+
if (classOf[SpecificRecordBase].isAssignableFrom(recordClass)) {
237+
val classModelField = recordClass.getDeclaredField("MODEL$")
238+
classModelField.setAccessible(true)
239+
classModelField.get(null).asInstanceOf[SpecificData]
240+
} else {
241+
SpecificData.get()
242+
}
243+
}
244+
238245
private class Identity[T](cls: Class[T])
239246
extends SimpleFunction[T, T](SerializableFunctions.identity[T]) {
240247
override def getInputTypeDescriptor: TypeDescriptor[T] = TypeDescriptor.of(cls)

0 commit comments

Comments
 (0)