github.com/parquet-go/parquet-go@v0.20.0/buffer.go (about) 1 package parquet 2 3 import ( 4 "log" 5 "reflect" 6 "runtime" 7 "sort" 8 "sync" 9 "sync/atomic" 10 11 "github.com/parquet-go/parquet-go/internal/debug" 12 ) 13 14 // GenericBuffer is similar to a Buffer but uses a type parameter to define the 15 // Go type representing the schema of rows in the buffer. 16 // 17 // See GenericWriter for details about the benefits over the classic Buffer API. 18 type GenericBuffer[T any] struct { 19 base Buffer 20 write bufferFunc[T] 21 } 22 23 // NewGenericBuffer is like NewBuffer but returns a GenericBuffer[T] suited to write 24 // rows of Go type T. 25 // 26 // The type parameter T should be a map, struct, or any. Any other types will 27 // cause a panic at runtime. Type checking is a lot more effective when the 28 // generic parameter is a struct type, using map and interface types is somewhat 29 // similar to using a Writer. If using an interface type for the type parameter, 30 // then providing a schema at instantiation is required. 31 // 32 // If the option list may explicitly declare a schema, it must be compatible 33 // with the schema generated from T. 34 func NewGenericBuffer[T any](options ...RowGroupOption) *GenericBuffer[T] { 35 config, err := NewRowGroupConfig(options...) 36 if err != nil { 37 panic(err) 38 } 39 40 t := typeOf[T]() 41 if config.Schema == nil && t != nil { 42 config.Schema = schemaOf(dereference(t)) 43 } 44 45 if config.Schema == nil { 46 panic("generic buffer must be instantiated with schema or concrete type.") 47 } 48 49 buf := &GenericBuffer[T]{ 50 base: Buffer{config: config}, 51 } 52 buf.base.configure(config.Schema) 53 buf.write = bufferFuncOf[T](t, config.Schema) 54 return buf 55 } 56 57 func typeOf[T any]() reflect.Type { 58 var v T 59 return reflect.TypeOf(v) 60 } 61 62 type bufferFunc[T any] func(*GenericBuffer[T], []T) (int, error) 63 64 func bufferFuncOf[T any](t reflect.Type, schema *Schema) bufferFunc[T] { 65 if t == nil { 66 return (*GenericBuffer[T]).writeRows 67 } 68 switch t.Kind() { 69 case reflect.Interface, reflect.Map: 70 return (*GenericBuffer[T]).writeRows 71 72 case reflect.Struct: 73 return makeBufferFunc[T](t, schema) 74 75 case reflect.Pointer: 76 if e := t.Elem(); e.Kind() == reflect.Struct { 77 return makeBufferFunc[T](t, schema) 78 } 79 } 80 panic("cannot create buffer for values of type " + t.String()) 81 } 82 83 func makeBufferFunc[T any](t reflect.Type, schema *Schema) bufferFunc[T] { 84 writeRows := writeRowsFuncOf(t, schema, nil) 85 return func(buf *GenericBuffer[T], rows []T) (n int, err error) { 86 err = writeRows(buf.base.columns, makeArrayOf(rows), columnLevels{}) 87 if err == nil { 88 n = len(rows) 89 } 90 return n, err 91 } 92 } 93 94 func (buf *GenericBuffer[T]) Size() int64 { 95 return buf.base.Size() 96 } 97 98 func (buf *GenericBuffer[T]) NumRows() int64 { 99 return buf.base.NumRows() 100 } 101 102 func (buf *GenericBuffer[T]) ColumnChunks() []ColumnChunk { 103 return buf.base.ColumnChunks() 104 } 105 106 func (buf *GenericBuffer[T]) ColumnBuffers() []ColumnBuffer { 107 return buf.base.ColumnBuffers() 108 } 109 110 func (buf *GenericBuffer[T]) SortingColumns() []SortingColumn { 111 return buf.base.SortingColumns() 112 } 113 114 func (buf *GenericBuffer[T]) Len() int { 115 return buf.base.Len() 116 } 117 118 func (buf *GenericBuffer[T]) Less(i, j int) bool { 119 return buf.base.Less(i, j) 120 } 121 122 func (buf *GenericBuffer[T]) Swap(i, j int) { 123 buf.base.Swap(i, j) 124 } 125 126 func (buf *GenericBuffer[T]) Reset() { 127 buf.base.Reset() 128 } 129 130 func (buf *GenericBuffer[T]) Write(rows []T) (int, error) { 131 if len(rows) == 0 { 132 return 0, nil 133 } 134 return buf.write(buf, rows) 135 } 136 137 func (buf *GenericBuffer[T]) WriteRows(rows []Row) (int, error) { 138 return buf.base.WriteRows(rows) 139 } 140 141 func (buf *GenericBuffer[T]) WriteRowGroup(rowGroup RowGroup) (int64, error) { 142 return buf.base.WriteRowGroup(rowGroup) 143 } 144 145 func (buf *GenericBuffer[T]) Rows() Rows { 146 return buf.base.Rows() 147 } 148 149 func (buf *GenericBuffer[T]) Schema() *Schema { 150 return buf.base.Schema() 151 } 152 153 func (buf *GenericBuffer[T]) writeRows(rows []T) (int, error) { 154 if cap(buf.base.rowbuf) < len(rows) { 155 buf.base.rowbuf = make([]Row, len(rows)) 156 } else { 157 buf.base.rowbuf = buf.base.rowbuf[:len(rows)] 158 } 159 defer clearRows(buf.base.rowbuf) 160 161 schema := buf.base.Schema() 162 for i := range rows { 163 buf.base.rowbuf[i] = schema.Deconstruct(buf.base.rowbuf[i], &rows[i]) 164 } 165 166 return buf.base.WriteRows(buf.base.rowbuf) 167 } 168 169 var ( 170 _ RowGroup = (*GenericBuffer[any])(nil) 171 _ RowGroupWriter = (*GenericBuffer[any])(nil) 172 _ sort.Interface = (*GenericBuffer[any])(nil) 173 174 _ RowGroup = (*GenericBuffer[struct{}])(nil) 175 _ RowGroupWriter = (*GenericBuffer[struct{}])(nil) 176 _ sort.Interface = (*GenericBuffer[struct{}])(nil) 177 178 _ RowGroup = (*GenericBuffer[map[struct{}]struct{}])(nil) 179 _ RowGroupWriter = (*GenericBuffer[map[struct{}]struct{}])(nil) 180 _ sort.Interface = (*GenericBuffer[map[struct{}]struct{}])(nil) 181 ) 182 183 // Buffer represents an in-memory group of parquet rows. 184 // 185 // The main purpose of the Buffer type is to provide a way to sort rows before 186 // writing them to a parquet file. Buffer implements sort.Interface as a way 187 // to support reordering the rows that have been written to it. 188 type Buffer struct { 189 config *RowGroupConfig 190 schema *Schema 191 rowbuf []Row 192 colbuf [][]Value 193 chunks []ColumnChunk 194 columns []ColumnBuffer 195 sorted []ColumnBuffer 196 } 197 198 // NewBuffer constructs a new buffer, using the given list of buffer options 199 // to configure the buffer returned by the function. 200 // 201 // The function panics if the buffer configuration is invalid. Programs that 202 // cannot guarantee the validity of the options passed to NewBuffer should 203 // construct the buffer configuration independently prior to calling this 204 // function: 205 // 206 // config, err := parquet.NewRowGroupConfig(options...) 207 // if err != nil { 208 // // handle the configuration error 209 // ... 210 // } else { 211 // // this call to create a buffer is guaranteed not to panic 212 // buffer := parquet.NewBuffer(config) 213 // ... 214 // } 215 func NewBuffer(options ...RowGroupOption) *Buffer { 216 config, err := NewRowGroupConfig(options...) 217 if err != nil { 218 panic(err) 219 } 220 buf := &Buffer{ 221 config: config, 222 } 223 if config.Schema != nil { 224 buf.configure(config.Schema) 225 } 226 return buf 227 } 228 229 func (buf *Buffer) configure(schema *Schema) { 230 if schema == nil { 231 return 232 } 233 sortingColumns := buf.config.Sorting.SortingColumns 234 buf.sorted = make([]ColumnBuffer, len(sortingColumns)) 235 236 forEachLeafColumnOf(schema, func(leaf leafColumn) { 237 nullOrdering := nullsGoLast 238 columnIndex := int(leaf.columnIndex) 239 columnType := leaf.node.Type() 240 bufferCap := buf.config.ColumnBufferCapacity 241 dictionary := (Dictionary)(nil) 242 encoding := encodingOf(leaf.node) 243 244 if isDictionaryEncoding(encoding) { 245 estimatedDictBufferSize := columnType.EstimateSize(bufferCap) 246 dictBuffer := columnType.NewValues( 247 make([]byte, 0, estimatedDictBufferSize), 248 nil, 249 ) 250 dictionary = columnType.NewDictionary(columnIndex, 0, dictBuffer) 251 columnType = dictionary.Type() 252 } 253 254 sortingIndex := searchSortingColumn(sortingColumns, leaf.path) 255 if sortingIndex < len(sortingColumns) && sortingColumns[sortingIndex].NullsFirst() { 256 nullOrdering = nullsGoFirst 257 } 258 259 column := columnType.NewColumnBuffer(columnIndex, bufferCap) 260 switch { 261 case leaf.maxRepetitionLevel > 0: 262 column = newRepeatedColumnBuffer(column, leaf.maxRepetitionLevel, leaf.maxDefinitionLevel, nullOrdering) 263 case leaf.maxDefinitionLevel > 0: 264 column = newOptionalColumnBuffer(column, leaf.maxDefinitionLevel, nullOrdering) 265 } 266 buf.columns = append(buf.columns, column) 267 268 if sortingIndex < len(sortingColumns) { 269 if sortingColumns[sortingIndex].Descending() { 270 column = &reversedColumnBuffer{column} 271 } 272 buf.sorted[sortingIndex] = column 273 } 274 }) 275 276 buf.schema = schema 277 buf.rowbuf = make([]Row, 0, 1) 278 buf.colbuf = make([][]Value, len(buf.columns)) 279 buf.chunks = make([]ColumnChunk, len(buf.columns)) 280 281 for i, column := range buf.columns { 282 buf.chunks[i] = column 283 } 284 } 285 286 // Size returns the estimated size of the buffer in memory (in bytes). 287 func (buf *Buffer) Size() int64 { 288 size := int64(0) 289 for _, col := range buf.columns { 290 size += col.Size() 291 } 292 return size 293 } 294 295 // NumRows returns the number of rows written to the buffer. 296 func (buf *Buffer) NumRows() int64 { return int64(buf.Len()) } 297 298 // ColumnChunks returns the buffer columns. 299 func (buf *Buffer) ColumnChunks() []ColumnChunk { return buf.chunks } 300 301 // ColumnBuffer returns the buffer columns. 302 // 303 // This method is similar to ColumnChunks, but returns a list of ColumnBuffer 304 // instead of a ColumnChunk values (the latter being read-only); calling 305 // ColumnBuffers or ColumnChunks with the same index returns the same underlying 306 // objects, but with different types, which removes the need for making a type 307 // assertion if the program needed to write directly to the column buffers. 308 // The presence of the ColumnChunks method is still required to satisfy the 309 // RowGroup interface. 310 func (buf *Buffer) ColumnBuffers() []ColumnBuffer { return buf.columns } 311 312 // Schema returns the schema of the buffer. 313 // 314 // The schema is either configured by passing a Schema in the option list when 315 // constructing the buffer, or lazily discovered when the first row is written. 316 func (buf *Buffer) Schema() *Schema { return buf.schema } 317 318 // SortingColumns returns the list of columns by which the buffer will be 319 // sorted. 320 // 321 // The sorting order is configured by passing a SortingColumns option when 322 // constructing the buffer. 323 func (buf *Buffer) SortingColumns() []SortingColumn { return buf.config.Sorting.SortingColumns } 324 325 // Len returns the number of rows written to the buffer. 326 func (buf *Buffer) Len() int { 327 if len(buf.columns) == 0 { 328 return 0 329 } else { 330 // All columns have the same number of rows. 331 return buf.columns[0].Len() 332 } 333 } 334 335 // Less returns true if row[i] < row[j] in the buffer. 336 func (buf *Buffer) Less(i, j int) bool { 337 for _, col := range buf.sorted { 338 switch { 339 case col.Less(i, j): 340 return true 341 case col.Less(j, i): 342 return false 343 } 344 } 345 return false 346 } 347 348 // Swap exchanges the rows at indexes i and j. 349 func (buf *Buffer) Swap(i, j int) { 350 for _, col := range buf.columns { 351 col.Swap(i, j) 352 } 353 } 354 355 // Reset clears the content of the buffer, allowing it to be reused. 356 func (buf *Buffer) Reset() { 357 for _, col := range buf.columns { 358 col.Reset() 359 } 360 } 361 362 // Write writes a row held in a Go value to the buffer. 363 func (buf *Buffer) Write(row interface{}) error { 364 if buf.schema == nil { 365 buf.configure(SchemaOf(row)) 366 } 367 368 buf.rowbuf = buf.rowbuf[:1] 369 defer clearRows(buf.rowbuf) 370 371 buf.rowbuf[0] = buf.schema.Deconstruct(buf.rowbuf[0], row) 372 _, err := buf.WriteRows(buf.rowbuf) 373 return err 374 } 375 376 // WriteRows writes parquet rows to the buffer. 377 func (buf *Buffer) WriteRows(rows []Row) (int, error) { 378 defer func() { 379 for i, colbuf := range buf.colbuf { 380 clearValues(colbuf) 381 buf.colbuf[i] = colbuf[:0] 382 } 383 }() 384 385 if buf.schema == nil { 386 return 0, ErrRowGroupSchemaMissing 387 } 388 389 for _, row := range rows { 390 for _, value := range row { 391 columnIndex := value.Column() 392 buf.colbuf[columnIndex] = append(buf.colbuf[columnIndex], value) 393 } 394 } 395 396 for columnIndex, values := range buf.colbuf { 397 if _, err := buf.columns[columnIndex].WriteValues(values); err != nil { 398 // TODO: an error at this stage will leave the buffer in an invalid 399 // state since the row was partially written. Applications are not 400 // expected to continue using the buffer after getting an error, 401 // maybe we can enforce it? 402 return 0, err 403 } 404 } 405 406 return len(rows), nil 407 } 408 409 // WriteRowGroup satisfies the RowGroupWriter interface. 410 func (buf *Buffer) WriteRowGroup(rowGroup RowGroup) (int64, error) { 411 rowGroupSchema := rowGroup.Schema() 412 switch { 413 case rowGroupSchema == nil: 414 return 0, ErrRowGroupSchemaMissing 415 case buf.schema == nil: 416 buf.configure(rowGroupSchema) 417 case !nodesAreEqual(buf.schema, rowGroupSchema): 418 return 0, ErrRowGroupSchemaMismatch 419 } 420 if !sortingColumnsHavePrefix(rowGroup.SortingColumns(), buf.SortingColumns()) { 421 return 0, ErrRowGroupSortingColumnsMismatch 422 } 423 n := buf.NumRows() 424 r := rowGroup.Rows() 425 defer r.Close() 426 _, err := CopyRows(bufferWriter{buf}, r) 427 return buf.NumRows() - n, err 428 } 429 430 // Rows returns a reader exposing the current content of the buffer. 431 // 432 // The buffer and the returned reader share memory. Mutating the buffer 433 // concurrently to reading rows may result in non-deterministic behavior. 434 func (buf *Buffer) Rows() Rows { return newRowGroupRows(buf, ReadModeSync) } 435 436 // bufferWriter is an adapter for Buffer which implements both RowWriter and 437 // PageWriter to enable optimizations in CopyRows for types that support writing 438 // rows by copying whole pages instead of calling WriteRow repeatedly. 439 type bufferWriter struct{ buf *Buffer } 440 441 func (w bufferWriter) WriteRows(rows []Row) (int, error) { 442 return w.buf.WriteRows(rows) 443 } 444 445 func (w bufferWriter) WriteValues(values []Value) (int, error) { 446 return w.buf.columns[values[0].Column()].WriteValues(values) 447 } 448 449 func (w bufferWriter) WritePage(page Page) (int64, error) { 450 return CopyValues(w.buf.columns[page.Column()], page.Values()) 451 } 452 453 var ( 454 _ RowGroup = (*Buffer)(nil) 455 _ RowGroupWriter = (*Buffer)(nil) 456 _ sort.Interface = (*Buffer)(nil) 457 458 _ RowWriter = (*bufferWriter)(nil) 459 _ PageWriter = (*bufferWriter)(nil) 460 _ ValueWriter = (*bufferWriter)(nil) 461 ) 462 463 type buffer struct { 464 data []byte 465 refc uintptr 466 pool *bufferPool 467 stack []byte 468 } 469 470 func (b *buffer) refCount() int { 471 return int(atomic.LoadUintptr(&b.refc)) 472 } 473 474 func (b *buffer) ref() { 475 atomic.AddUintptr(&b.refc, +1) 476 } 477 478 func (b *buffer) unref() { 479 if atomic.AddUintptr(&b.refc, ^uintptr(0)) == 0 { 480 if b.pool != nil { 481 b.pool.put(b) 482 } 483 } 484 } 485 486 func monitorBufferRelease(b *buffer) { 487 if rc := b.refCount(); rc != 0 { 488 log.Printf("PARQUETGODEBUG: buffer garbage collected with non-zero reference count\n%s", string(b.stack)) 489 } 490 } 491 492 type bufferPool struct { 493 // Buckets are split in two groups for short and large buffers. In the short 494 // buffer group (below 256KB), the growth rate between each bucket is 2. The 495 // growth rate changes to 1.5 in the larger buffer group. 496 // 497 // Short buffer buckets: 498 // --------------------- 499 // 4K, 8K, 16K, 32K, 64K, 128K, 256K 500 // 501 // Large buffer buckets: 502 // --------------------- 503 // 364K, 546K, 819K ... 504 // 505 buckets [bufferPoolBucketCount]sync.Pool 506 } 507 508 func (p *bufferPool) newBuffer(bufferSize, bucketSize int) *buffer { 509 b := &buffer{ 510 data: make([]byte, bufferSize, bucketSize), 511 refc: 1, 512 pool: p, 513 } 514 if debug.TRACEBUF > 0 { 515 b.stack = make([]byte, 4096) 516 runtime.SetFinalizer(b, monitorBufferRelease) 517 } 518 return b 519 } 520 521 // get returns a buffer from the levelled buffer pool. size is used to choose 522 // the appropriate pool. 523 func (p *bufferPool) get(bufferSize int) *buffer { 524 bucketIndex, bucketSize := bufferPoolBucketIndexAndSizeOfGet(bufferSize) 525 526 b := (*buffer)(nil) 527 if bucketIndex >= 0 { 528 b, _ = p.buckets[bucketIndex].Get().(*buffer) 529 } 530 531 if b == nil { 532 b = p.newBuffer(bufferSize, bucketSize) 533 } else { 534 b.data = b.data[:bufferSize] 535 b.ref() 536 } 537 538 if debug.TRACEBUF > 0 { 539 b.stack = b.stack[:runtime.Stack(b.stack[:cap(b.stack)], false)] 540 } 541 return b 542 } 543 544 func (p *bufferPool) put(b *buffer) { 545 if b.pool != p { 546 panic("BUG: buffer returned to a different pool than the one it was allocated from") 547 } 548 if b.refCount() != 0 { 549 panic("BUG: buffer returned to pool with a non-zero reference count") 550 } 551 if bucketIndex, _ := bufferPoolBucketIndexAndSizeOfPut(cap(b.data)); bucketIndex >= 0 { 552 p.buckets[bucketIndex].Put(b) 553 } 554 } 555 556 const ( 557 bufferPoolBucketCount = 32 558 bufferPoolMinSize = 4096 559 bufferPoolLastShortBucketSize = 262144 560 ) 561 562 func bufferPoolNextSize(size int) int { 563 if size < bufferPoolLastShortBucketSize { 564 return size * 2 565 } else { 566 return size + (size / 2) 567 } 568 } 569 570 func bufferPoolBucketIndexAndSizeOfGet(size int) (int, int) { 571 limit := bufferPoolMinSize 572 573 for i := 0; i < bufferPoolBucketCount; i++ { 574 if size <= limit { 575 return i, limit 576 } 577 limit = bufferPoolNextSize(limit) 578 } 579 580 return -1, size 581 } 582 583 func bufferPoolBucketIndexAndSizeOfPut(size int) (int, int) { 584 // When releasing buffers, some may have a capacity that is not one of the 585 // bucket sizes (due to the use of append for example). In this case, we 586 // have to put the buffer is the highest bucket with a size less or equal 587 // to the buffer capacity. 588 if limit := bufferPoolMinSize; size >= limit { 589 for i := 0; i < bufferPoolBucketCount; i++ { 590 n := bufferPoolNextSize(limit) 591 if size < n { 592 return i, limit 593 } 594 limit = n 595 } 596 } 597 return -1, size 598 } 599 600 var ( 601 buffers bufferPool 602 ) 603 604 type bufferedPage struct { 605 Page 606 values *buffer 607 offsets *buffer 608 repetitionLevels *buffer 609 definitionLevels *buffer 610 } 611 612 func newBufferedPage(page Page, values, offsets, definitionLevels, repetitionLevels *buffer) *bufferedPage { 613 p := &bufferedPage{ 614 Page: page, 615 values: values, 616 offsets: offsets, 617 definitionLevels: definitionLevels, 618 repetitionLevels: repetitionLevels, 619 } 620 bufferRef(values) 621 bufferRef(offsets) 622 bufferRef(definitionLevels) 623 bufferRef(repetitionLevels) 624 return p 625 } 626 627 func (p *bufferedPage) Slice(i, j int64) Page { 628 return newBufferedPage( 629 p.Page.Slice(i, j), 630 p.values, 631 p.offsets, 632 p.definitionLevels, 633 p.repetitionLevels, 634 ) 635 } 636 637 func (p *bufferedPage) Retain() { 638 bufferRef(p.values) 639 bufferRef(p.offsets) 640 bufferRef(p.definitionLevels) 641 bufferRef(p.repetitionLevels) 642 } 643 644 func (p *bufferedPage) Release() { 645 bufferUnref(p.values) 646 bufferUnref(p.offsets) 647 bufferUnref(p.definitionLevels) 648 bufferUnref(p.repetitionLevels) 649 } 650 651 func bufferRef(buf *buffer) { 652 if buf != nil { 653 buf.ref() 654 } 655 } 656 657 func bufferUnref(buf *buffer) { 658 if buf != nil { 659 buf.unref() 660 } 661 } 662 663 // Retain is a helper function to increment the reference counter of pages 664 // backed by memory which can be granularly managed by the application. 665 // 666 // Usage of this function is optional and with Release, is intended to allow 667 // finer grain memory management in the application. Most programs should be 668 // able to rely on automated memory management provided by the Go garbage 669 // collector instead. 670 // 671 // The function should be called when a page lifetime is about to be shared 672 // between multiple goroutines or layers of an application, and the program 673 // wants to express "sharing ownership" of the page. 674 // 675 // Calling this function on pages that do not embed a reference counter does 676 // nothing. 677 func Retain(page Page) { 678 if p, _ := page.(retainable); p != nil { 679 p.Retain() 680 } 681 } 682 683 // Release is a helper function to decrement the reference counter of pages 684 // backed by memory which can be granularly managed by the application. 685 // 686 // Usage of this is optional and with Retain, is intended to allow finer grained 687 // memory management in the application, at the expense of potentially causing 688 // panics if the page is used after its reference count has reached zero. Most 689 // programs should be able to rely on automated memory management provided by 690 // the Go garbage collector instead. 691 // 692 // The function should be called to return a page to the internal buffer pool, 693 // when a goroutine "releases ownership" it acquired either by being the single 694 // owner (e.g. capturing the return value from a ReadPage call) or having gotten 695 // shared ownership by calling Retain. 696 // 697 // Calling this function on pages that do not embed a reference counter does 698 // nothing. 699 func Release(page Page) { 700 if p, _ := page.(releasable); p != nil { 701 p.Release() 702 } 703 } 704 705 type retainable interface { 706 Retain() 707 } 708 709 type releasable interface { 710 Release() 711 } 712 713 var ( 714 _ retainable = (*bufferedPage)(nil) 715 _ releasable = (*bufferedPage)(nil) 716 )