-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Implement Client/Server Editing #1043
Conversation
… not sure if this works for the model but we will find out
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.
This is a really, really cool feature, with awesome test coverage also!
Apologies for the long delay in reviewing and merging this. In my defense - I think a PR having its own Changelog
is a pretty apt jump-the-shark moment in terms of monolithic PRs, even if only as this PR's exceptionally detailed description nor mini-Changelog
will not show up in the lerna-changelog
master CHANGELOG.md
for releases. Which means you won't be able to reap those sweet, sweet internet points.
Totally worth the wait, this one PR is basically an entire release's worth of features in itself!
@@ -597,10 +646,16 @@ t_gnode::get_table_sptr() { | |||
void | |||
t_gnode::promote_column(const std::string& name, t_dtype new_type) { | |||
PSP_TRACE_SENTINEL(); | |||
PSP_VERBOSE_ASSERT(m_init, "touching uninited object"); | |||
PSP_VERBOSE_ASSERT(m_init, "Cannot `promote_column` on an uninited gnode."); |
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.
Much better error message! I assume you changed these to trace the segfault? Reminds me that a future improvement may be to completely replace PSP_VERBOSE_ASSERT
which check object initialization only and replace with proper valgrind support in the build scripts, stricter clang warnings and our existing excellent test coverage?
t_uindex | ||
t_gnode::num_output_ports() const { | ||
return m_oports.size(); | ||
} |
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 am OK with removing these too if you're keen.
@@ -32,7 +32,7 @@ const UpperLowerCaseTokenType = createToken({ | |||
}); | |||
|
|||
// Create tokens for column names and computed function names | |||
const column_name_regex_pattern = /(["'])(?<column_name>.*?[^\\])\1/y; | |||
const column_name_regex_pattern = /(["'])(.*?[^\\])\1/y; |
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.
Chrome bug?
|
||
// Whether the user should be notified - False if process_table exited | ||
// early, True otherwise. | ||
return result.m_should_notify_userspace; |
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.
We could maybe even remove the concept of no-ops and drop m_should_notify_userspace
altogether; instead, we can just say if _process_table
made it past the empty-table-checks earlier in this method, we call notify_userspace
. This may simplify some of the aggregate logic in the context_*
classes as well since they won't need to report this information.
@@ -34,7 +34,7 @@ const JPMC_VERSIONS = [ | |||
|
|||
const FINOS_VERSIONS = ["0.3.1", "0.3.0", "0.3.0-rc.3", "0.3.0-rc.2", "0.3.0-rc.1"]; | |||
|
|||
const UMD_VERSIONS = ["0.4.5", "0.4.4", "0.4.2", "0.4.1", "0.4.0", "0.3.9", "0.3.8", "0.3.7", "0.3.6"]; | |||
const UMD_VERSIONS = ["0.4.8", "0.4.7", "0.4.6", "0.4.5", "0.4.4", "0.4.2", "0.4.1", "0.4.0", "0.3.9", "0.3.8", "0.3.7", "0.3.6"]; |
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.
Thanks for updating this - reminds me, we could just use this and dump the pre-finos versions I think now?
$ npm view @finos/perspective versions --json
[
"0.3.0-rc.1",
"0.3.0-rc.2",
"0.3.0-rc.3",
"0.3.0-rc.4",
"0.3.0",
"0.3.1",
"0.3.4",
"0.3.5",
"0.3.6",
"0.3.7",
"0.3.8",
"0.3.9",
"0.4.0-rc.1",
"0.4.0-rc.2",
"0.4.0-rc.3",
"0.4.0-rc.4",
"0.4.0-rc.5",
"0.4.0-rc.6",
"0.4.0",
"0.4.1",
"0.4.2",
"0.4.4",
"0.4.5",
"0.4.6",
"0.4.7",
"0.4.8"
]
* @async | ||
*/ | ||
async getEditPort() { | ||
return this._edit_port_lock; |
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.
This doesn't need to be async
- _edit_port_lock
is a Promise
.
|
||
// Resolve the edit port lock, which allows for `get_edit_port` to be | ||
// called in arbitary order without ever returning a null value. | ||
this._edit_port_lock.resolve(this._edit_port); |
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 this is unnecessary if we remove the await
on line 272 above? I would also like to understand how this can lead to null
for port, since it seems (on inspection) that the Promise
does not defer the actual call to make_port()
.
I tried removing this on my branch and did not see errors in the tests, but the message implies this error condition is race-y ..
@@ -153,7 +153,8 @@ export class Server { | |||
// post transferable data for arrow | |||
if (msg.args && msg.args[0]) { | |||
if (msg.method === "on_update" && msg.args[0]["mode"] === "row") { | |||
this.post(result, [ev]); | |||
// actual arrow is in the `delta` | |||
this.post(result, [ev.delta]); |
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.
Most Understated Addition in this patch!
Thanks for the PR! |
One of Perspective's main selling points is interoperability between
perspective-viewer
in the browser andperspective-python
on the server. This PR furthers inter-op by enabling editing between a client and a server without the latency overhead of using a remoteTable
, i.e. proxying all operations to the server. By creatingports
on the client and server, clients are able to make edits to a PerspectiveTable
in browser memory, and automatically transfer their edits to the server and all other connected clients. Similarly, the server is able to propagate live updates to each of their connected clients, and state will remain automatically synchronized.For a concise example, see the Python Examples folder, where the
client_server_edits
Python and HTML files show the simplest possible implementation of client/server editing. The benefits of using client/server editing as opposed to traditional remote table editing are numerous, including:The main tradeoff is loading time - remote tables load incredibly quickly, as there is no data to transport across the network besides what is being requested by the viewer. For a table to be constructed in the client-side, however, the client needs access to the entire table's dataset in Arrow form. The example provides a simple implementation of this, and for most datasets the tradeoff in loading time is far outweighed by increased performance.
Changelog
make_port
API toTable
, and thegetEditPort
method toperspective-viewer
, allowing clients and servers to create ports and enable client/server editing.on_update
API - depending on the language, parameters toon_update
callback functions need to be changed in order for proper operation. issues with callback parameters are easily debuggable, as they will raise exceptions in both Javascript and Python.port_id
anddelta
, depending on ifcell
orrow
mode is selected:port_id
argument by default, or they receive bothport_id
anddelta
(if therow
mode was selected):process
batched updates and notify users inon_update
, which will always fire with a port_id.perspective-viewer
so that each viewer automatically gets a port that is used for editing.PerspectiveWebsocket
(in JS) andPerspectiveTornadohandler
(in Python).