mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-17 03:24:40 -07:00
JetStream first pass basics.
This is the first checkin for JetStream. Has some rudimentary basics working. TODO 1. Push vs pull mode for observables. (work queues) 2. Disk/File store, memory only for now. 3. clustering code - design shaping up well. 4. Finalize account import semantics. 5. Lots of other little things. Signed-off-by: Derek Collison <derek@nats.io>
This commit is contained in:
@@ -44,6 +44,8 @@ const (
|
||||
SYSTEM
|
||||
// LEAF is for leaf node connections.
|
||||
LEAF
|
||||
// JETSTREAM is an internal jetstream client.
|
||||
JETSTREAM
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -382,6 +384,7 @@ type subscription struct {
|
||||
client *client
|
||||
im *streamImport // This is for import stream support.
|
||||
shadow []*subscription // This is to track shadowed accounts.
|
||||
icb msgHandler
|
||||
subject []byte
|
||||
queue []byte
|
||||
sid []byte
|
||||
@@ -489,6 +492,8 @@ func (c *client) initClient() {
|
||||
c.ncs = fmt.Sprintf("%s - lid:%d", conn, c.cid)
|
||||
case SYSTEM:
|
||||
c.ncs = "SYSTEM"
|
||||
case JETSTREAM:
|
||||
c.ncs = "JETSTREAM"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,6 +818,7 @@ func (c *client) writeLoop() {
|
||||
// message that now is being delivered.
|
||||
func (c *client) flushClients(budget time.Duration) time.Time {
|
||||
last := time.Now()
|
||||
|
||||
// Check pending clients for flush.
|
||||
for cp := range c.pcd {
|
||||
// TODO(dlc) - Wonder if it makes more sense to create a new map?
|
||||
@@ -976,7 +982,7 @@ func (c *client) readLoop() {
|
||||
return
|
||||
}
|
||||
|
||||
if cpacc && start.Sub(lpacc) >= closedSubsCheckInterval {
|
||||
if cpacc && (start.Sub(lpacc)) >= closedSubsCheckInterval {
|
||||
c.pruneClosedSubFromPerAccountCache()
|
||||
lpacc = time.Now()
|
||||
}
|
||||
@@ -1053,15 +1059,17 @@ func (c *client) flushOutbound() bool {
|
||||
c.mu.Unlock()
|
||||
|
||||
// flush here
|
||||
now := time.Now()
|
||||
start := time.Now()
|
||||
|
||||
// FIXME(dlc) - writev will do multiple IOs past 1024 on
|
||||
// most platforms, need to account for that with deadline?
|
||||
nc.SetWriteDeadline(now.Add(wdl))
|
||||
nc.SetWriteDeadline(start.Add(wdl))
|
||||
|
||||
// Actual write to the socket.
|
||||
n, err := nb.WriteTo(nc)
|
||||
nc.SetWriteDeadline(time.Time{})
|
||||
lft := time.Since(now)
|
||||
|
||||
lft := time.Since(start)
|
||||
|
||||
// Re-acquire client lock.
|
||||
c.mu.Lock()
|
||||
@@ -1561,7 +1569,7 @@ func (c *client) maxPayloadViolation(sz int, max int32) {
|
||||
}
|
||||
|
||||
// queueOutbound queues data for a clientconnection.
|
||||
// Return if the data is referenced or not. If referenced, the caller
|
||||
// Returns if the data is referenced or not. If referenced, the caller
|
||||
// should not reuse the `data` array.
|
||||
// Lock should be held.
|
||||
func (c *client) queueOutbound(data []byte) bool {
|
||||
@@ -1933,8 +1941,8 @@ func (c *client) processSub(argo []byte, noForward bool) (*subscription, error)
|
||||
|
||||
sid := string(sub.sid)
|
||||
|
||||
// This check does not apply to SYSTEM clients (because they don't have a `nc`...)
|
||||
if kind != SYSTEM && c.isClosed() {
|
||||
// This check does not apply to SYSTEM or JETSTREAM clients (because they don't have a `nc`...)
|
||||
if c.isClosed() && (kind != SYSTEM && kind != JETSTREAM) {
|
||||
c.mu.Unlock()
|
||||
return sub, nil
|
||||
}
|
||||
@@ -2490,11 +2498,15 @@ func (c *client) deliverMsg(sub *subscription, subject, mh, msg []byte, gwrply b
|
||||
atomic.AddInt64(&srv.outMsgs, 1)
|
||||
atomic.AddInt64(&srv.outBytes, msgSize)
|
||||
|
||||
// Check for internal subscription.
|
||||
if client.kind == SYSTEM {
|
||||
// Check for internal subscriptions.
|
||||
if client.kind == SYSTEM || client.kind == JETSTREAM {
|
||||
s := client.srv
|
||||
client.mu.Unlock()
|
||||
s.deliverInternalMsg(sub, c, subject, c.pa.reply, msg[:msgSize])
|
||||
if sub.icb == nil {
|
||||
s.Debugf("Received internal callback with no registered handler")
|
||||
return false
|
||||
}
|
||||
sub.icb(sub, c, string(subject), string(c.pa.reply), msg[:msgSize])
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2865,7 +2877,13 @@ func (c *client) processInboundClientMsg(msg []byte) {
|
||||
atomic.LoadInt64(&c.srv.gateway.totalQSubs) > 0 {
|
||||
flag |= pmrCollectQueueNames
|
||||
}
|
||||
qnames = c.processMsgResults(c.acc, r, msg, c.pa.subject, c.pa.reply, flag)
|
||||
// With JetStream we now have times where we want to match a subsctiption
|
||||
// on one subject, but deliver it with abother. e.g. JetStream deliverables.
|
||||
subj := c.pa.subject
|
||||
if len(c.pa.deliver) > 0 {
|
||||
subj = c.pa.deliver
|
||||
}
|
||||
qnames = c.processMsgResults(c.acc, r, msg, subj, c.pa.reply, flag)
|
||||
}
|
||||
|
||||
// Now deal with gateways
|
||||
@@ -3526,6 +3544,14 @@ func (c *client) closeConnection(reason ClosedState) {
|
||||
func (c *client) teardownConn() {
|
||||
c.mu.Lock()
|
||||
|
||||
// Be consistent with the creation: for routes and gateways,
|
||||
// we use Noticef on create, so use that too for delete.
|
||||
if c.kind == ROUTER || c.kind == GATEWAY {
|
||||
c.Noticef("%s connection closed", c.typeString())
|
||||
} else { // Client, System, Jetstream and Leafnode connections.
|
||||
c.Debugf("%s connection closed", c.typeString())
|
||||
}
|
||||
|
||||
c.clearAuthTimer()
|
||||
c.clearPingTimer()
|
||||
// Unblock anyone who is potentially stalled waiting on us.
|
||||
@@ -3576,8 +3602,8 @@ func (c *client) teardownConn() {
|
||||
|
||||
c.mu.Unlock()
|
||||
|
||||
// Remove client's or leaf node subscriptions.
|
||||
if (kind == CLIENT || kind == LEAF) && acc != nil {
|
||||
// Remove client's or leaf node or jetstream subscriptions.
|
||||
if acc != nil && (kind == CLIENT || kind == LEAF || kind == JETSTREAM) {
|
||||
acc.sl.RemoveBatch(subs)
|
||||
} else if kind == ROUTER {
|
||||
go c.removeRemoteSubs()
|
||||
|
||||
Reference in New Issue
Block a user