-
Notifications
You must be signed in to change notification settings - Fork 8
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
Add Component Locking #102
Conversation
7d9e267
to
56627ba
Compare
96d3409
to
e05fefe
Compare
I have now implemented the four proposed locking features: Query, Acquire, Release, and Subscribe (see the PR description). I've also added an example of locking locations in This is not yet ready for merge since it requires an ably-js release including ably/ably-js#1418, but it is otherwise ready for review. |
Thanks @lmars , a few questions/ comments:
|
I decided to explicitly type
Correct,
When a member calls Typical usage is like: const req = space.locks.acquire(id);
// req.status is PENDING
// wait for a status change
const status = await new Promise<LockStatus>(resolve => {
space.locks.subscribe('update', lock => {
if (lock.request.id == req.id) {
resolve(lock.request.status);
}
});
});
if (status == LockStatus.LOCKED) {
// member got the lock
} else {
// member did not get the lock
}
It will, but we don't emit an event for a lock becoming |
Yes, but why do others need to know about someone's failed attempt to lock a component through subscriptions? |
This encompasses the foundation of what we discussed in our work. I've left a couple of small comments, questions, but I think this is a great first iteration of the API. |
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
56627ba
to
e58bcc4
Compare
@dpiatek I've addressed your comments, PTAL |
@lmars two small comments otherwise LGTM |
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
77d2dc8
to
86712a5
Compare
@mattheworiordan I had requested your review but I've gone ahead and merged this, please feel free to leave comments after the fact though and I can follow up in future PRs. |
Yup, sorry, I've not had time. I will add comments in a separate post or inline shortly. |
|
||
export const ERR_LOCK_RELEASED = new ErrorInfo({ | ||
message: 'lock was released', | ||
code: 40053, |
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.
When should we start adding these error codes to ably-common. My concern is if we leave it to the end, we may forget / have codes scattered across SDKs. Should we perhaps consider doing this when we merge things to main
?
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 they should be added alongside the PR yes, which I didn't do here, but I did ticket it as a priority for next sprint: https://ably.atlassian.net/browse/MMB-238.
I think we might want to reserve a range specifically for Spaces, like we'd done for Asset Tracking.
{ | ||
id: lockID, | ||
status: 'pending', | ||
timestamp: Date.now(), |
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.
What timestamp format are we planning on supporting when this goes server-side? Would it be a JS timestamp value and we'll assume all other languages use that?
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.
It's going to be a millisecond timestamp, at least, and might at some point extend to being a timeserial
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.
Yeh, millisecond unix timestamp, I'll document this in the feature spec next sprint (https://ably.atlassian.net/browse/MMB-235).
} | ||
} | ||
|
||
processPresenceMessage(message: Types.PresenceMessage) { |
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.
Shouldn't this be private? Not sure on support for this, but I thought the recommended approach is #
prefix?
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.
It's called by the Space
class here, so it can't be private no.
Thanks for this @lmars, great start and simple which is nice.
I am finding it hard what to comment on or not in terms of functionality because you're not saying which parts of the implementation you have omitted, vs decided not to implement. This makes it hard for me to comment on what's not here as a result. On the call, for example, I mentioned you had not included a way to iterate over the locks, and you said you'd add that. Had you decided not to include that method, or just not implemented it yet? Given this, I feel it would be a waste of time to comment on what's not here, and would prefer to do that once you present an IDL of a complete API. Would it make sense to have that evolve as you do PRs, so we can comment on what you're removing/adding/changing to the complete API as well?
Referring to Paddy's proposal, he stated:
Do you envisage userData is just part of
I am not mad about the attribute In this code example: const isLocked = space.locks.get(lockId) !== undefined;
if (isLocked) {
info('location is already locked');
process.exit();
} I have two comments/questions:
if (space.locks.isLocked(lockId, exclude_self: true) {
info('location is already locked');
process.exit();
} is just nicer to read and more intuitive. ❓ Should we close https://github.com/ably/ideas/issues/459 now that this work supersedes it? |
Apologies, that initial description was when this PR was a draft for early feedback, I subsequently implemented all functionality from our internal API design doc (i.e. Query, Acquire, Release, and Subscribe), which this now covers. If there is anything missing from the design doc, please point it out, but I realise I omitted
Can you clarify what you mean by IDL? Something beyond what is in the API design doc? I wasn't planning to write anything beyond that.
Yes,
Yes we could have
Roger that, I'll write something up specific to enhancing the Query implementation rather than discussing here.
I'll do that once I've written the server-side implementation plan (https://ably.atlassian.net/browse/MMB-197). |
See the "Interface Definition" of the core SDK spec or the TypeScript definition of ably-js. In one place, we can see the API surface for an SDK, and when reviewing individual suggestions around what an API should look like, we can see how that fits with the other APIs. There is no practical way as we working for anyone to see how the overall API surface is shaping up and reflect on how consistent and/or intuitive it is. I realise your work is just in locking, so admittedly this is not your problem. However, I do believe it's a problem more broadly for this SDK, and any other product SDKs we develop, when we don't have a single TypeScript definition / or IDL-like definition we can look at in totality. What are your thoughts on this? @stmoreau and @Srushtika WDYT, coming from the perspective of how we operationalise the maintenance and development of SDKs more generally?
Perhaps one for @paddybyers to comment on given he was the one who suggested it. My sense is we should either reject changes once a lock is obtained, or provide an API to actually make and subscribe to lock attribute changes. |
I think the main aim of having the userdata is that participants can rely on the state set there being globally authoritative for the channel. If it can change, then that just detracts from that - to be useful, there would need then to be explicit versioning, events when it changes, etc. So I think, at least for now, we should say that it's immutable. We could consider it being mutable but that would require us thinking about what API a user would really need around that |
This is a draft PR to get feedback on an early implementation of Component Locking.
This is not a complete implementation, but opening for early feedback and to align with changes proposed in #101.
[UPDATE: 2023-08-07]
I have now implemented all four proposed locking features: Query, Acquire, Release, and Subscribe.
Query
space.locks.get
is used to query whether a lock identifier is currently locked and by whom. It returns aLock
type which has the following fields:For example:
Acquire
space.locks.acquire
initialises a lock request, adds it to thelocks
field of presence message extras, and callspresence.update
.It returns a Promise which resolves once
presence.update
resolves.It throws an error if a lock request already exists for the given identifier with a status of
PENDING
orLOCKED
.Release
space.locks.release
releases a previously requested lock by removing it from thelocks
field of presence message extras, and callspresence.update
.It returns a Promise which resolves once
presence.update
resolves.Subscribe
space.locks.subscribe
subscribes to changes in lock status across all members.The callback is called with a value of type
Lock
.Such changes occur when:
PENDING
request transitions toLOCKED
(i.e. the requesting member now holds the lock)PENDING
request transitions toUNLOCKED
(i.e. the requesting member does not hold the lock since another member already does)LOCKED
request transitions toUNLOCKED
(i.e. the lock was either released or invalidated by a concurrent request which took precedence)UNLOCKED
request transitions toLOCKED
(i.e. the requesting member reacquired a lock)I've also added an example in
examples/locks.ts
which can be run withnpm run examples:locks
:MMB-213 [MMB-214] [MMB-215]