@@ -99,6 +99,7 @@ public class QwpUdpSender implements Sender {
9999 private int estimateColumnCount ;
100100 private QwpTableBuffer .ColumnBuffer [] missingColumns = new QwpTableBuffer .ColumnBuffer [8 ];
101101 private int missingColumnCount ;
102+ private boolean replayingRowJournal ;
102103 private int rowJournalSize ;
103104 private long runningEstimate ;
104105
@@ -158,6 +159,7 @@ public void cancelRow() {
158159 checkNotClosed ();
159160 if (currentTableBuffer != null ) {
160161 currentTableBuffer .cancelCurrentRow ();
162+ currentTableBuffer .rollbackUncommittedColumns ();
161163 rollbackEstimateToCommitted ();
162164 }
163165 rowJournalSize = 0 ;
@@ -169,6 +171,7 @@ public void close() {
169171 try {
170172 if (hasInProgressRow ()) {
171173 currentTableBuffer .cancelCurrentRow ();
174+ currentTableBuffer .rollbackUncommittedColumns ();
172175 rollbackEstimateToCommitted ();
173176 rowJournalSize = 0 ;
174177 }
@@ -419,12 +422,13 @@ public Sender timestampColumn(CharSequence columnName, Instant value) {
419422
420423 private QwpTableBuffer .ColumnBuffer acquireColumn (CharSequence name , byte type , boolean nullable ) {
421424 QwpTableBuffer .ColumnBuffer col = currentTableBuffer .getExistingColumn (name , type );
422- if (col == null && currentTableBuffer .getRowCount () > 0 ) {
425+ assert !replayingRowJournal || currentTableBuffer .getRowCount () == 0 ;
426+ if (col == null && !replayingRowJournal && currentTableBuffer .getRowCount () > 0 ) {
423427 if (currentRowColumnCount > 0 ) {
424- throw new LineSenderException ("schema change in middle of row is not supported" );
428+ flushCommittedRowsAndReplayCurrentRow ();
429+ } else {
430+ flushSingleTable (currentTableName , currentTableBuffer );
425431 }
426- flushSingleTable (currentTableName , currentTableBuffer );
427- col = currentTableBuffer .getOrCreateColumn (name , type , nullable );
428432 }
429433
430434 if (col == null ) {
@@ -444,7 +448,7 @@ private void appendBooleanColumn(CharSequence name, boolean value, boolean addJo
444448 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
445449 currentRowColumnCount ++;
446450
447- if (addJournal && trackDatagramEstimate ) {
451+ if (shouldJournal ( addJournal ) ) {
448452 ColumnEntry e = nextJournalEntry ();
449453 e .kind = ENTRY_BOOL ;
450454 e .name = col .getName ();
@@ -462,7 +466,7 @@ private void appendDecimal128Column(CharSequence name, Decimal128 value, boolean
462466 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
463467 currentRowColumnCount ++;
464468
465- if (addJournal && trackDatagramEstimate ) {
469+ if (shouldJournal ( addJournal ) ) {
466470 ColumnEntry e = nextJournalEntry ();
467471 e .kind = ENTRY_DECIMAL128 ;
468472 e .name = col .getName ();
@@ -480,7 +484,7 @@ private void appendDecimal256Column(CharSequence name, Decimal256 value, boolean
480484 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
481485 currentRowColumnCount ++;
482486
483- if (addJournal && trackDatagramEstimate ) {
487+ if (shouldJournal ( addJournal ) ) {
484488 ColumnEntry e = nextJournalEntry ();
485489 e .kind = ENTRY_DECIMAL256 ;
486490 e .name = col .getName ();
@@ -498,7 +502,7 @@ private void appendDecimal64Column(CharSequence name, Decimal64 value, boolean a
498502 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
499503 currentRowColumnCount ++;
500504
501- if (addJournal && trackDatagramEstimate ) {
505+ if (shouldJournal ( addJournal ) ) {
502506 ColumnEntry e = nextJournalEntry ();
503507 e .kind = ENTRY_DECIMAL64 ;
504508 e .name = col .getName ();
@@ -531,7 +535,7 @@ private void appendDesignatedTimestamp(long value, boolean nanos, boolean addJou
531535 long payloadDelta = (long ) (col .getValueCount () - valueCountBefore ) * 8 ;
532536 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
533537
534- if (addJournal && trackDatagramEstimate ) {
538+ if (shouldJournal ( addJournal ) ) {
535539 ColumnEntry e = nextJournalEntry ();
536540 e .kind = nanos ? ENTRY_AT_NANOS : ENTRY_AT_MICROS ;
537541 e .longValue = value ;
@@ -567,7 +571,7 @@ private void appendDoubleArrayColumn(CharSequence name, Object value, boolean ad
567571 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
568572 currentRowColumnCount ++;
569573
570- if (addJournal && trackDatagramEstimate ) {
574+ if (shouldJournal ( addJournal ) ) {
571575 ColumnEntry e = nextJournalEntry ();
572576 e .kind = ENTRY_DOUBLE_ARRAY ;
573577 e .name = col .getName ();
@@ -585,7 +589,7 @@ private void appendDoubleColumn(CharSequence name, double value, boolean addJour
585589 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
586590 currentRowColumnCount ++;
587591
588- if (addJournal && trackDatagramEstimate ) {
592+ if (shouldJournal ( addJournal ) ) {
589593 ColumnEntry e = nextJournalEntry ();
590594 e .kind = ENTRY_DOUBLE ;
591595 e .name = col .getName ();
@@ -622,7 +626,7 @@ private void appendLongArrayColumn(CharSequence name, Object value, boolean addJ
622626 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
623627 currentRowColumnCount ++;
624628
625- if (addJournal && trackDatagramEstimate ) {
629+ if (shouldJournal ( addJournal ) ) {
626630 ColumnEntry e = nextJournalEntry ();
627631 e .kind = ENTRY_LONG_ARRAY ;
628632 e .name = col .getName ();
@@ -640,7 +644,7 @@ private void appendLongColumn(CharSequence name, long value, boolean addJournal)
640644 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
641645 currentRowColumnCount ++;
642646
643- if (addJournal && trackDatagramEstimate ) {
647+ if (shouldJournal ( addJournal ) ) {
644648 ColumnEntry e = nextJournalEntry ();
645649 e .kind = ENTRY_LONG ;
646650 e .name = col .getName ();
@@ -660,7 +664,7 @@ private void appendStringColumn(CharSequence name, CharSequence value, boolean a
660664 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
661665 currentRowColumnCount ++;
662666
663- if (addJournal && trackDatagramEstimate ) {
667+ if (shouldJournal ( addJournal ) ) {
664668 ColumnEntry e = nextJournalEntry ();
665669 e .kind = ENTRY_STRING ;
666670 e .name = col .getName ();
@@ -679,7 +683,7 @@ private void appendSymbolColumn(CharSequence name, CharSequence value, boolean a
679683 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
680684 currentRowColumnCount ++;
681685
682- if (addJournal && trackDatagramEstimate ) {
686+ if (shouldJournal ( addJournal ) ) {
683687 ColumnEntry e = nextJournalEntry ();
684688 e .kind = ENTRY_SYMBOL ;
685689 e .name = col .getName ();
@@ -697,7 +701,7 @@ private void appendTimestampColumn(CharSequence name, byte type, long value, byt
697701 applyValueEstimate (col , sizeBefore , col .getSize (), payloadDelta );
698702 currentRowColumnCount ++;
699703
700- if (addJournal && trackDatagramEstimate ) {
704+ if (shouldJournal ( addJournal ) ) {
701705 ColumnEntry e = nextJournalEntry ();
702706 e .kind = journalKind ;
703707 e .name = col .getName ();
@@ -927,6 +931,14 @@ private void flushSingleTable(String tableName, QwpTableBuffer tableBuffer) {
927931 resetEstimateState ();
928932 }
929933
934+ private void flushCommittedRowsAndReplayCurrentRow () {
935+ currentTableBuffer .cancelCurrentRow ();
936+ currentTableBuffer .rollbackUncommittedColumns ();
937+ rollbackEstimateToCommitted ();
938+ flushSingleTable (currentTableName , currentTableBuffer );
939+ replayRowJournal ();
940+ }
941+
930942 private void maybeAutoFlush () {
931943 if (runningEstimate <= maxDatagramSize ) {
932944 return ;
@@ -936,11 +948,7 @@ private void maybeAutoFlush() {
936948 throw singleRowTooLarge (runningEstimate );
937949 }
938950
939- currentTableBuffer .cancelCurrentRow ();
940- rollbackEstimateToCommitted ();
941-
942- flushSingleTable (currentTableName , currentTableBuffer );
943- replayRowJournal ();
951+ flushCommittedRowsAndReplayCurrentRow ();
944952 collectMissingColumns (currentTableBuffer .getRowCount () + 1 );
945953
946954 if (runningEstimate > maxDatagramSize ) {
@@ -986,27 +994,33 @@ private ColumnEntry nextJournalEntry() {
986994 }
987995
988996 private void replayRowJournal () {
989- for (int i = 0 ; i < rowJournalSize ; i ++) {
990- ColumnEntry entry = rowJournal .getQuick (i );
991- switch (entry .kind ) {
992- case ENTRY_AT_MICROS -> appendDesignatedTimestamp (entry .longValue , false , false );
993- case ENTRY_AT_NANOS -> appendDesignatedTimestamp (entry .longValue , true , false );
994- case ENTRY_BOOL -> appendBooleanColumn (entry .name , entry .boolValue , false );
995- case ENTRY_DECIMAL128 -> appendDecimal128Column (entry .name , (Decimal128 ) entry .objectValue , false );
996- case ENTRY_DECIMAL256 -> appendDecimal256Column (entry .name , (Decimal256 ) entry .objectValue , false );
997- case ENTRY_DECIMAL64 -> appendDecimal64Column (entry .name , (Decimal64 ) entry .objectValue , false );
998- case ENTRY_DOUBLE -> appendDoubleColumn (entry .name , entry .doubleValue , false );
999- case ENTRY_DOUBLE_ARRAY -> appendDoubleArrayColumn (entry .name , entry .objectValue , false );
1000- case ENTRY_LONG -> appendLongColumn (entry .name , entry .longValue , false );
1001- case ENTRY_LONG_ARRAY -> appendLongArrayColumn (entry .name , entry .objectValue , false );
1002- case ENTRY_STRING -> appendStringColumn (entry .name , entry .stringValue , false );
1003- case ENTRY_SYMBOL -> appendSymbolColumn (entry .name , entry .stringValue , false );
1004- case ENTRY_TIMESTAMP_COL_MICROS ->
1005- appendTimestampColumn (entry .name , TYPE_TIMESTAMP , entry .longValue , ENTRY_TIMESTAMP_COL_MICROS , false );
1006- case ENTRY_TIMESTAMP_COL_NANOS ->
1007- appendTimestampColumn (entry .name , TYPE_TIMESTAMP_NANOS , entry .longValue , ENTRY_TIMESTAMP_COL_NANOS , false );
1008- default -> throw new LineSenderException ("unknown row journal entry type: " + entry .kind );
997+ assert currentTableBuffer .getRowCount () == 0 : "row journal replay requires a reset table buffer" ;
998+ replayingRowJournal = true ;
999+ try {
1000+ for (int i = 0 ; i < rowJournalSize ; i ++) {
1001+ ColumnEntry entry = rowJournal .getQuick (i );
1002+ switch (entry .kind ) {
1003+ case ENTRY_AT_MICROS -> appendDesignatedTimestamp (entry .longValue , false , false );
1004+ case ENTRY_AT_NANOS -> appendDesignatedTimestamp (entry .longValue , true , false );
1005+ case ENTRY_BOOL -> appendBooleanColumn (entry .name , entry .boolValue , false );
1006+ case ENTRY_DECIMAL128 -> appendDecimal128Column (entry .name , (Decimal128 ) entry .objectValue , false );
1007+ case ENTRY_DECIMAL256 -> appendDecimal256Column (entry .name , (Decimal256 ) entry .objectValue , false );
1008+ case ENTRY_DECIMAL64 -> appendDecimal64Column (entry .name , (Decimal64 ) entry .objectValue , false );
1009+ case ENTRY_DOUBLE -> appendDoubleColumn (entry .name , entry .doubleValue , false );
1010+ case ENTRY_DOUBLE_ARRAY -> appendDoubleArrayColumn (entry .name , entry .objectValue , false );
1011+ case ENTRY_LONG -> appendLongColumn (entry .name , entry .longValue , false );
1012+ case ENTRY_LONG_ARRAY -> appendLongArrayColumn (entry .name , entry .objectValue , false );
1013+ case ENTRY_STRING -> appendStringColumn (entry .name , entry .stringValue , false );
1014+ case ENTRY_SYMBOL -> appendSymbolColumn (entry .name , entry .stringValue , false );
1015+ case ENTRY_TIMESTAMP_COL_MICROS ->
1016+ appendTimestampColumn (entry .name , TYPE_TIMESTAMP , entry .longValue , ENTRY_TIMESTAMP_COL_MICROS , false );
1017+ case ENTRY_TIMESTAMP_COL_NANOS ->
1018+ appendTimestampColumn (entry .name , TYPE_TIMESTAMP_NANOS , entry .longValue , ENTRY_TIMESTAMP_COL_NANOS , false );
1019+ default -> throw new LineSenderException ("unknown row journal entry type: " + entry .kind );
1020+ }
10091021 }
1022+ } finally {
1023+ replayingRowJournal = false ;
10101024 }
10111025 }
10121026
@@ -1030,6 +1044,10 @@ private void rollbackEstimateToCommitted() {
10301044 missingColumnCount = 0 ;
10311045 }
10321046
1047+ private boolean shouldJournal (boolean addJournal ) {
1048+ return addJournal && (trackDatagramEstimate || currentTableBuffer .getRowCount () > 0 || rowJournalSize > 0 );
1049+ }
1050+
10331051 private LineSenderException singleRowTooLarge (long estimate ) {
10341052 return new LineSenderException (
10351053 "single row exceeds maximum datagram size (" + maxDatagramSize
0 commit comments