youtubebeat/vendor/github.com/elastic/beats/packetbeat/protos/cassandra/trans.go

227 lines
4.7 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package cassandra
import (
"time"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/packetbeat/procs"
"github.com/elastic/beats/packetbeat/protos/applayer"
)
type transactions struct {
config *transactionConfig
requests messageList
responses messageList
onTransaction transactionHandler
}
type transactionConfig struct {
transactionTimeout time.Duration
}
type transactionHandler func(requ, resp *message) error
// List of messages available for correlation
type messageList struct {
head, tail *message
}
func (trans *transactions) init(c *transactionConfig, cb transactionHandler) {
trans.config = c
trans.onTransaction = cb
}
func (trans *transactions) onMessage(
tuple *common.IPPortTuple,
dir uint8,
msg *message,
) error {
var err error
msg.Tuple = *tuple
msg.Transport = applayer.TransportTCP
msg.CmdlineTuple = procs.ProcWatcher.FindProcessesTupleTCP(&msg.Tuple)
if msg.IsRequest {
if isDebug {
debugf("Received request with tuple: %s", tuple)
}
err = trans.onRequest(tuple, dir, msg)
} else {
if isDebug {
debugf("Received response with tuple: %s", tuple)
}
err = trans.onResponse(tuple, dir, msg)
}
return err
}
// onRequest handles request messages, merging with incomplete requests
// and adding non-merged requests into the correlation list.
func (trans *transactions) onRequest(
tuple *common.IPPortTuple,
dir uint8,
msg *message,
) error {
prev := trans.requests.last()
merged, err := trans.tryMergeRequests(prev, msg)
if err != nil {
return err
}
if merged {
msg = prev
} else {
trans.requests.append(msg)
}
if !msg.isComplete {
return nil
}
return trans.correlate()
}
// onRequest handles response messages, merging with incomplete requests
// and adding non-merged responses into the correlation list.
func (trans *transactions) onResponse(
tuple *common.IPPortTuple,
dir uint8,
msg *message,
) error {
prev := trans.responses.last()
merged, err := trans.tryMergeResponses(prev, msg)
if err != nil {
return err
}
if merged {
msg = prev
} else {
trans.responses.append(msg)
}
if !msg.isComplete {
return nil
}
return trans.correlate()
}
func (trans *transactions) tryMergeRequests(
prev, msg *message,
) (merged bool, err error) {
msg.isComplete = true
return false, nil
}
func (trans *transactions) tryMergeResponses(prev, msg *message) (merged bool, err error) {
msg.isComplete = true
return false, nil
}
func (trans *transactions) correlate() error {
requests := &trans.requests
responses := &trans.responses
// drop responses with missing requests
if requests.empty() {
for !responses.empty() {
//if the response is EVENT, which pushed from server, we can accept that
resp := responses.first()
if !resp.isComplete {
break
}
if resp.header["op"] == "EVENT" {
if isDebug {
logp.Debug("cassandra", "server pushed message,%v", resp.header)
}
responses.pop()
if err := trans.onTransaction(nil, resp); err != nil {
return err
}
return nil
}
logp.Warn("Response from unknown transaction. Ignoring. %v", resp.header)
responses.pop()
}
return nil
}
// merge requests with responses into transactions
for !responses.empty() && !requests.empty() {
resp := responses.first()
if !resp.isComplete {
break
}
requ := requests.pop()
responses.pop()
if err := trans.onTransaction(requ, resp); err != nil {
return err
}
}
return nil
}
func (ml *messageList) append(msg *message) {
if ml.tail == nil {
ml.head = msg
} else {
ml.tail.next = msg
}
msg.next = nil
ml.tail = msg
}
func (ml *messageList) empty() bool {
return ml.head == nil
}
func (ml *messageList) pop() *message {
if ml.head == nil {
return nil
}
msg := ml.head
ml.head = ml.head.next
if ml.head == nil {
ml.tail = nil
}
return msg
}
func (ml *messageList) first() *message {
return ml.head
}
func (ml *messageList) last() *message {
return ml.tail
}