-
Notifications
You must be signed in to change notification settings - Fork 818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Persisted unsafe queues implementation #691
Conversation
This work looks really interesting. How far do you think you are till the first integration with Moquette? |
I think we are close, just want to change the API of |
Then integrate into Moquette, with feature flag to enable it and create a new release. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not gone though the complete code yet, but added some intermediate questions...
* */ | ||
public Optional<ByteBuffer> dequeue() throws QueueException { | ||
if (!currentHeadPtr.isGreaterThan(currentTailPtr)) { | ||
if (currentTailPtr.compareTo(currentHeadPtr) > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For symmetry, could use currentTailPtr.isGreaterThan(currentHeadPtr)
instead of a direct compare.
private CrossSegmentHeaderResult decodeCrossHeader(Segment segment, VirtualPointer pointer) throws QueueException { | ||
// read first part | ||
ByteBuffer lengthBuffer = ByteBuffer.allocate(LENGTH_HEADER_SIZE); | ||
final ByteBuffer partialHeader = segment.readAllBytesAfter(pointer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be significantly more efficient to make the segment read methods accept an existing Buffer to put the data into, instead of having those read methods create new buffers that are then copied into the already existing buffer?
|
||
private final QueuePool queuePool; | ||
private final PagedFilesAllocator.AllocationListener allocationListener; | ||
private final ReentrantLock lock = new ReentrantLock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the queue is explicitly not thread-safe, then what is the function of this lock? It's only used in the (de)queue methods, when reading cross-segment data. The comments call it a global lock, but this seems to be a queue-local lock... I would expect a global lock to be instantiated in the QueuePool. But there it should be enough to shortly lock the QueuePool in the nextFreeSegment()
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that to correctly manage the handling of the queue segments the data structures used to track those has to be accessed serially.
The data structures that manage the segment allocation, free and reassignment are:
queueSegments
recycledSegments
inQueuePool
class.
The access should be serialized only operating phase, when various session threads ask for next free segment.
However we can't leave uncovered by lock in other cases, like queue definition loads, during QueuePool
instantiation, during close or during the retrieval of next tail segment of a queue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm little wrong. The lock should guard only the creation or reuse of a Segment, so only recycledSegments
are part of the critical section, not queueSegments
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand the following correctly:
- Each single Queue is only accessed by a single Thread
- The QueuePool assigns segments to Queues
- Once a Segment is assigned to a Queue, only this queue touches the segment
- A queue can return a segment to the Pool. It is then added to recycledSegments.
I suspect the lock is only needed when:
- a Queue requests a segment from the pool, since it would be bad if two Queues get the same segment.
- closing/deleting an entire page (if that ever happens) since it would be bad if a page is deleted with a segment that a Queue just received.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you understand right
f987236
to
3ad5cc6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy new Year!
Looks good. I only noticed one unused field :)
I guess the next step is to implement the different repositories based on this?
|
||
private static final Logger LOG = LoggerFactory.getLogger(QueuePool.class); | ||
|
||
static final boolean queueDebug = Boolean.parseBoolean(System.getProperty("moquette.queue.debug", "false")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, no, it's used in two other classes.
…ith idiomatic asci chars
bf1dfe8
to
8043765
Compare
Release notes
Implements a disk persistent queues system that is not thread safe.
What it does?
Create the implementation of segmented queues. The data is stored in mmapped memory pages, where each page is divided in segments. Each queue own a list of segments. The access to the each segment is intended to be done by the same thread, so no serialization is needed. The only critical section, guarded by a lock, is the interaction to require or release segment.