@@ -139,6 +139,14 @@ public void testBoundedSenderMixedNullablePaddingPreservesRowsAndPacketLimit() t
139139 });
140140 }
141141
142+ @ Test
143+ public void testEstimateMatchesActualEncodedSize () throws Exception {
144+ assertMemoryLeak (() -> {
145+ auditEstimateWithStableSchemaAndNullableValues ();
146+ auditEstimateAcrossSymbolDictionaryVarintBoundary ();
147+ });
148+ }
149+
142150 @ Test
143151 public void testBoundedSenderNullableStringNullAcrossOverflowBoundaryPreservesRowsAndPacketLimit () throws Exception {
144152 assertMemoryLeak (() -> {
@@ -1702,6 +1710,91 @@ private static DoubleArrayValue doubleArrayValue(int[] shape, double... values)
17021710 return new DoubleArrayValue (dims , elems );
17031711 }
17041712
1713+ private static void auditEstimateAcrossSymbolDictionaryVarintBoundary () throws Exception {
1714+ ArrayList <ScenarioRow > rows = new ArrayList <>();
1715+ for (int i = 0 ; i < 160 ; i ++) {
1716+ final int rowId = i ;
1717+ rows .add (row (
1718+ "sym_audit" ,
1719+ sender -> sender .table ("sym_audit" )
1720+ .longColumn ("x" , rowId )
1721+ .symbol ("sym" , "sym-" + rowId )
1722+ .atNow (),
1723+ "x" , (long ) rowId ,
1724+ "sym" , "sym-" + rowId
1725+ ));
1726+ }
1727+ assertEstimateAtLeastActual (rows );
1728+ }
1729+
1730+ private static void auditEstimateWithStableSchemaAndNullableValues () throws Exception {
1731+ ArrayList <ScenarioRow > rows = new ArrayList <>();
1732+ for (int i = 0 ; i < 96 ; i ++) {
1733+ final int rowId = i ;
1734+ final String stringValue = (i & 1 ) == 0 ? "tokyo-" + i + "-" + repeat ('x' , (i % 31 ) + 1 ) : null ;
1735+ final long [] longArray = i % 3 == 0 ? new long []{i , i + 1L , i + 2L } : null ;
1736+ final double [][] doubleArray = i % 5 == 0 ? new double [][]{{i + 0.5 , i + 1.5 }, {i + 2.5 , i + 3.5 }} : null ;
1737+ final Decimal64 decimal64 = i % 7 == 0 ? Decimal64 .fromLong (i * 100L + 7 , 2 ) : null ;
1738+ final Decimal128 decimal128 = i % 11 == 0 ? Decimal128 .fromLong (i * 1000L + 11 , 4 ) : null ;
1739+ final Decimal256 decimal256 = i % 13 == 0 ? Decimal256 .fromLong (i * 10000L + 13 , 3 ) : null ;
1740+
1741+ rows .add (row (
1742+ "audit" ,
1743+ sender -> {
1744+ sender .table ("audit" )
1745+ .longColumn ("l" , rowId )
1746+ .doubleColumn ("d" , rowId + 0.25 )
1747+ .symbol ("sym" , "stable" );
1748+ if (stringValue != null ) {
1749+ sender .stringColumn ("s" , stringValue );
1750+ }
1751+ if (longArray != null ) {
1752+ sender .longArray ("la" , longArray );
1753+ }
1754+ if (doubleArray != null ) {
1755+ sender .doubleArray ("da" , doubleArray );
1756+ }
1757+ if (decimal64 != null ) {
1758+ sender .decimalColumn ("d64" , decimal64 );
1759+ }
1760+ if (decimal128 != null ) {
1761+ sender .decimalColumn ("d128" , decimal128 );
1762+ }
1763+ if (decimal256 != null ) {
1764+ sender .decimalColumn ("d256" , decimal256 );
1765+ }
1766+ sender .at (rowId + 1L , ChronoUnit .MICROS );
1767+ },
1768+ "l" , (long ) rowId ,
1769+ "d" , rowId + 0.25 ,
1770+ "sym" , "stable" ,
1771+ "s" , stringValue ,
1772+ "la" , longArray == null ? null : longArrayValue (shape (longArray .length ), longArray ),
1773+ "da" , doubleArray == null ? null : doubleArrayValue (shape (doubleArray .length , doubleArray [0 ].length ), flatten (doubleArray )),
1774+ "d64" , decimal64 == null ? null : decimal (i * 100L + 7 , 2 ),
1775+ "d128" , decimal128 == null ? null : decimal (i * 1000L + 11 , 4 ),
1776+ "d256" , decimal256 == null ? null : decimal (i * 10000L + 13 , 3 ),
1777+ "" , rowId + 1L
1778+ ));
1779+ }
1780+ assertEstimateAtLeastActual (rows );
1781+ }
1782+
1783+ private static void assertEstimateAtLeastActual (List <ScenarioRow > rows ) throws Exception {
1784+ CapturingNetworkFacade nf = new CapturingNetworkFacade ();
1785+ try (QwpUdpSender sender = new QwpUdpSender (nf , 0 , 0 , 9000 , 1 , 1024 * 1024 )) {
1786+ for (int i = 0 ; i < rows .size (); i ++) {
1787+ rows .get (i ).writer .accept (sender );
1788+ long estimate = sender .committedDatagramEstimateForTest ();
1789+ long actual = fullPacketSize (rows .subList (0 , i + 1 ));
1790+ Assert .assertTrue (
1791+ "row " + i + " estimate underflow: estimate=" + estimate + ", actual=" + actual ,
1792+ estimate >= actual
1793+ );
1794+ }
1795+ }
1796+ }
1797+
17051798 private static List <DecodedRow > expectedRows (List <ScenarioRow > rows ) {
17061799 ArrayList <DecodedRow > expected = new ArrayList <>(rows .size ());
17071800 for (ScenarioRow row : rows ) {
0 commit comments