88 "math/rand"
99 "net"
1010 "regexp"
11+ "strconv"
1112 "strings"
1213 "sync"
1314 "sync/atomic"
@@ -23,6 +24,7 @@ import (
2324
2425 "github.com/singlestore-labs/events/eventmodels"
2526 "github.com/singlestore-labs/events/internal"
27+ "github.com/singlestore-labs/events/internal/multi"
2628 "github.com/singlestore-labs/events/internal/pwork"
2729)
2830
@@ -94,6 +96,20 @@ type Library[ID eventmodels.AbstractID[ID], TX eventmodels.AbstractTX, DB eventm
9496}
9597
9698type LibraryNoDB struct {
99+ // --- Size cap subsystem fields (producer-only, decoupled from topic creation) ---
100+ // Global broker caps
101+ sizeCapBrokerState atomic.Int32
102+ sizeCapBrokerLock sync.Mutex // protects changes to sizeCapBrokerState
103+ sizeCapBrokerLoadCtx * multi.Context
104+ sizeCapBrokerReady chan struct {}
105+ sizeCapDefaultAssumed int64 // anything smaller than this can be sent before knowing actual limits
106+ sizeCapBrokerMessageMax atomic.Int64 // message.max.bytes (0 unknown)
107+ sizeCapSocketRequestMax atomic.Int64 // socket.request.max.bytes (rarely limiting; 0 unknown)
108+ sizeCapWork pwork.Work [string , string ] // un-prefixed in APIs
109+ sizeCapTopicLimits gwrap.SyncMap [string , sizeCapTopicLimit ] // un-prefixed topic
110+
111+ // Per-topic limits map (separate from creatingTopic). Keys are topic names.
112+
97113 hasDB atomic.Bool
98114 tracer eventmodels.Tracer
99115 brokers []string
@@ -236,9 +252,13 @@ func New[ID eventmodels.AbstractID[ID], TX eventmodels.AbstractTX, DB eventmodel
236252 doEnhance : true ,
237253 instanceID : instanceCount .Add (1 ),
238254 topicsHaveBeenListed : make (chan struct {}),
255+ sizeCapBrokerReady : make (chan struct {}),
256+ sizeCapDefaultAssumed : 1_000_000 ,
257+ sizeCapBrokerLoadCtx : multi .New (),
239258 },
240259 }
241260 lib .configureTopicsPrework ()
261+ lib .configureSizeCapPrework ()
242262 return & lib
243263}
244264
@@ -311,6 +331,14 @@ func (lib *Library[ID, TX, DB]) SkipNotifierSupport() {
311331 lib .skipNotifier = true
312332}
313333
334+ // SetSizeCapLowerLimit overrides the default lower limit on sizes: any message under
335+ // this size can be sent before actual limits are known.
336+ func (lib * Library [ID , TX , DB ]) SetSizeCapLowerLimit (sizeCapDefaultAssumed int64 ) {
337+ lib .lock .Lock ()
338+ defer lib .lock .Unlock ()
339+ lib .sizeCapDefaultAssumed = sizeCapDefaultAssumed
340+ }
341+
314342// Configure sets up the Library so that it has the configuration it needs to run.
315343// The database connection is optional. Without it, certain features will always error:
316344//
@@ -630,6 +658,32 @@ func (lib *Library[ID, TX, DB]) Tracer() eventmodels.Tracer { return lib
630658// getController returns a client talking to the controlling broker. The
631659// controller is needed for certain requests, like creating a topic
632660func (lib * LibraryNoDB ) getController (ctx context.Context ) (_ * kafka.Client , err error ) {
661+ var c * kafka.Client
662+ err = lib .findABroker (ctx , func (conn * kafka.Conn ) error {
663+ controller , err := conn .Controller ()
664+ if err != nil {
665+ return errors .Errorf ("event library get controller from kafka connection: %w" , err )
666+ }
667+ ips , err := net .LookupIP (controller .Host )
668+ if err != nil {
669+ return errors .Errorf ("event library lookup IP of controller (%s): %w" , controller .Host , err )
670+ }
671+ if len (ips ) == 0 {
672+ return errors .Errorf ("event library lookup IP of controller (%s) got no addresses" , controller .Host )
673+ }
674+ c = & kafka.Client {
675+ Addr : & net.TCPAddr {
676+ IP : ips [0 ],
677+ Port : controller .Port ,
678+ },
679+ Transport : lib .transport (),
680+ }
681+ return nil
682+ })
683+ return c , err
684+ }
685+
686+ func (lib * LibraryNoDB ) findABroker (ctx context.Context , f func (* kafka.Conn ) error ) (err error ) {
633687 dialer := lib .dialer ()
634688 var tried int
635689 for _ , i := range rand .Perm (len (lib .brokers )) {
@@ -640,7 +694,7 @@ func (lib *LibraryNoDB) getController(ctx context.Context) (_ *kafka.Client, err
640694 lib .tracer .Logf ("[events] could not connect to broker %d (of %d) %s: %v" , i + 1 , len (lib .brokers ), broker , err )
641695 if tried == len (lib .brokers ) {
642696 // last broker, give up
643- return nil , errors .Errorf ("event library dial kafka broker (%s): %w" , broker , err )
697+ return errors .Errorf ("event library dial kafka broker (%s): %w" , broker , err )
644698 }
645699 continue
646700 }
@@ -650,27 +704,31 @@ func (lib *LibraryNoDB) getController(ctx context.Context) (_ *kafka.Client, err
650704 err = errors .Errorf ("event library close dialer (%s): %w" , lib .brokers [0 ], err )
651705 }
652706 }()
653- controller , err := conn .Controller ()
654- if err != nil {
655- return nil , errors .Errorf ("event library get controller from kafka connection: %w" , err )
656- }
657- ips , err := net .LookupIP (controller .Host )
658- if err != nil {
659- return nil , errors .Errorf ("event library lookup IP of controller (%s): %w" , controller .Host , err )
660- }
661- if len (ips ) == 0 {
662- return nil , errors .Errorf ("event library lookup IP of controller (%s) got no addresses" , controller .Host )
663- }
664- return & kafka.Client {
665- Addr : & net.TCPAddr {
666- IP : ips [0 ],
667- Port : controller .Port ,
668- },
669- Transport : lib .transport (),
670- }, nil
707+ return f (conn )
708+ }
709+ return errors .Errorf ("unexpected condition" )
710+ }
711+
712+ func (lib * LibraryNoDB ) getBrokers (ctx context.Context ) ([]kafka.Broker , error ) {
713+ var brokers []kafka.Broker
714+ err := lib .findABroker (ctx , func (conn * kafka.Conn ) error {
715+ var err error
716+ brokers , err = conn .Brokers ()
717+ return err
718+ })
719+ return brokers , err
720+ }
721+
722+ func (lib * LibraryNoDB ) getABrokerID (ctx context.Context ) (string , error ) {
723+ brokers , err := lib .getBrokers (ctx )
724+ if err != nil {
725+ return "" , errors .WithStack (err )
726+ }
727+ if len (brokers ) == 0 {
728+ return "" , errors .Errorf ("get brokers request returned no brokers" )
671729 }
672- // should not be able to get here
673- return nil , errors . Errorf ( "unexpected condition" )
730+ broker := brokers [ rand . Intn ( len ( brokers ))]
731+ return strconv . Itoa ( broker . ID ), nil
674732}
675733
676734// getConsumerGroupCoordinator returns a client talking to the control group's
0 commit comments