88 "math/rand"
99 "net"
1010 "regexp"
11+ "strconv"
1112 "strings"
1213 "sync"
1314 "sync/atomic"
@@ -93,6 +94,23 @@ type Library[ID eventmodels.AbstractID[ID], TX eventmodels.AbstractTX, DB eventm
9394}
9495
9596type LibraryNoDB struct {
97+ // --- Size cap subsystem fields (producer-only, decoupled from topic creation) ---
98+ // Global broker caps
99+ sizeCapBrokerState atomic.Int32 // 0=unstarted 1=loading 2=ready 3=failed
100+ sizeCapBrokerReady chan struct {}
101+ sizeCapBrokerMessageMax atomic.Int32 // message.max.bytes (0 unknown)
102+ sizeCapSocketRequestMax atomic.Int32 // socket.request.max.bytes (rarely limiting; 0 unknown)
103+ sizeCapBrokerErr atomic.Value // error or nil
104+ sizeCapDefaultAssumed int // anything smaller than this can be sent before knowing actual limits
105+
106+ // Precreated topics scan (enumeration of already seen topics; optional background)
107+ sizeCapPrecreatedState atomic.Int32 // 0=unstarted 1=running 2=done 3=failed
108+ sizeCapPrecreatedReady chan struct {}
109+ sizeCapPrecreatedErr atomic.Value
110+
111+ // Per-topic limits map (separate from creatingTopic). Keys are topic names.
112+ sizeCapTopicLimits gwrap.SyncMap [string , * sizeCapTopicLimit ]
113+
96114 hasDB atomic.Bool
97115 tracer eventmodels.Tracer
98116 brokers []string
@@ -233,6 +251,8 @@ func New[ID eventmodels.AbstractID[ID], TX eventmodels.AbstractTX, DB eventmodel
233251 doEnhance : true ,
234252 instanceID : instanceCount .Add (1 ),
235253 topicsHaveBeenListed : make (chan struct {}),
254+ sizeCapBrokerReady : make (chan struct {}),
255+ sizeCapDefaultAssumed : 1_000_000 ,
236256 },
237257 }
238258}
@@ -286,6 +306,14 @@ func (lib *Library[ID, TX, DB]) SetLazyTxProduce(lazy bool) {
286306 lib .lazyProduce = lazy
287307}
288308
309+ // SetSizeCapLowerLimit overrides the default lower limit on sizes: any message under
310+ // this size can be sent before actual limits are known.
311+ func (lib * Library [ID , TX , DB ]) SetSizeCapLowerLimit (sizeCapDefaultAssumed int ) {
312+ lib .lock .Lock ()
313+ defer lib .lock .Unlock ()
314+ lib .sizeCapDefaultAssumed = sizeCapDefaultAssumed
315+ }
316+
289317// Configure sets up the Library so that it has the configuration it needs to run.
290318// The database connection is optional. Without it, certain features will always error:
291319//
@@ -603,6 +631,32 @@ func (lib *Library[ID, TX, DB]) Tracer() eventmodels.Tracer { return lib
603631// getController returns a client talking to the controlling broker. The
604632// controller is needed for certain requests, like creating a topic
605633func (lib * LibraryNoDB ) getController (ctx context.Context ) (_ * kafka.Client , err error ) {
634+ var c * kafka.Client
635+ err = lib .findABroker (ctx , func (conn * kafka.Conn ) error {
636+ controller , err := conn .Controller ()
637+ if err != nil {
638+ return errors .Errorf ("event library get controller from kafka connection: %w" , err )
639+ }
640+ ips , err := net .LookupIP (controller .Host )
641+ if err != nil {
642+ return errors .Errorf ("event library lookup IP of controller (%s): %w" , controller .Host , err )
643+ }
644+ if len (ips ) == 0 {
645+ return errors .Errorf ("event library lookup IP of controller (%s) got no addresses" , controller .Host )
646+ }
647+ c = & kafka.Client {
648+ Addr : & net.TCPAddr {
649+ IP : ips [0 ],
650+ Port : controller .Port ,
651+ },
652+ Transport : lib .transport (),
653+ }
654+ return nil
655+ })
656+ return c , err
657+ }
658+
659+ func (lib * LibraryNoDB ) findABroker (ctx context.Context , f func (* kafka.Conn ) error ) (err error ) {
606660 dialer := lib .dialer ()
607661 var tried int
608662 for _ , i := range rand .Perm (len (lib .brokers )) {
@@ -613,7 +667,7 @@ func (lib *LibraryNoDB) getController(ctx context.Context) (_ *kafka.Client, err
613667 lib .tracer .Logf ("[events] could not connect to broker %d (of %d) %s: %v" , i + 1 , len (lib .brokers ), broker , err )
614668 if tried == len (lib .brokers ) {
615669 // last broker, give up
616- return nil , errors .Errorf ("event library dial kafka broker (%s): %w" , broker , err )
670+ return errors .Errorf ("event library dial kafka broker (%s): %w" , broker , err )
617671 }
618672 continue
619673 }
@@ -623,27 +677,31 @@ func (lib *LibraryNoDB) getController(ctx context.Context) (_ *kafka.Client, err
623677 err = errors .Errorf ("event library close dialer (%s): %w" , lib .brokers [0 ], err )
624678 }
625679 }()
626- controller , err := conn .Controller ()
627- if err != nil {
628- return nil , errors .Errorf ("event library get controller from kafka connection: %w" , err )
629- }
630- ips , err := net .LookupIP (controller .Host )
631- if err != nil {
632- return nil , errors .Errorf ("event library lookup IP of controller (%s): %w" , controller .Host , err )
633- }
634- if len (ips ) == 0 {
635- return nil , errors .Errorf ("event library lookup IP of controller (%s) got no addresses" , controller .Host )
636- }
637- return & kafka.Client {
638- Addr : & net.TCPAddr {
639- IP : ips [0 ],
640- Port : controller .Port ,
641- },
642- Transport : lib .transport (),
643- }, nil
680+ return f (conn )
681+ }
682+ return errors .Errorf ("unexpected condition" )
683+ }
684+
685+ func (lib * LibraryNoDB ) getBrokers (ctx context.Context ) ([]kafka.Broker , error ) {
686+ var brokers []kafka.Broker
687+ err := lib .findABroker (ctx , func (conn * kafka.Conn ) error {
688+ var err error
689+ brokers , err = conn .Brokers ()
690+ return err
691+ })
692+ return brokers , err
693+ }
694+
695+ func (lib * LibraryNoDB ) getABrokerID (ctx context.Context ) (string , error ) {
696+ brokers , err := lib .getBrokers (ctx )
697+ if err != nil {
698+ return "" , errors .WithStack (err )
699+ }
700+ if len (brokers ) == 0 {
701+ return "" , errors .Errorf ("get brokers request returned no brokers" )
644702 }
645- // should not be able to get here
646- return nil , errors . Errorf ( "unexpected condition" )
703+ broker := brokers [ rand . Intn ( len ( brokers ))]
704+ return strconv . Itoa ( broker . ID ), nil
647705}
648706
649707// getConsumerGroupCoordinator returns a client talking to the control group's
0 commit comments