-
Notifications
You must be signed in to change notification settings - Fork 158
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
[RFC] Dynamic databases #231
[RFC] Dynamic databases #231
Conversation
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.
Did a preliminary review. So far this looks reasonable, but I must dig deeper to really understand the KeyIndex indirection. In general, I must say it was (and, after this rfc, probably still is) hard to follow salsa's trait setup....
```rust,ignore | ||
pub trait DatabaseExt { | ||
#[allow(unused_variables)] | ||
fn query<Q>(&self, query: Q) -> QueryTable<'_, Self, Q> |
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.
Alternative is to add where Self: Sized
bound. I think for rust-analyzer we only invoke query
for final DB
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, interesting, that's true, we could do that, though it seems strictly less good. This is an interesting case in the language, it'd be nice if we had a way to express "final" functions in a trait that could not be overridden by impls (which would then be exempted from object safety requirements).
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.
In the latest version, I would up with TheQuery.in_db(&db)
instead. This works better because we need to coerce the &db
type to a &dyn DB
anyway. It's also nicely ergonomic and avoids turbofish. Not as discoverable. We could, side note, add a method like db.xxx_query()
that returns the QueryTable
.
Regarding following the setup, it is tricky, I was thinking about how to diagram it but I couldn't quite come up with the right diagram to explain everything. I didn't put a lot of effort into it yet, but I think it'd be worth it. |
I can probably put in some effort into prototyping this but I'd also be happy to work with someone else on that. Also, I forgot that I had planned to add a few notes in the alternatives section about ways we could collect |
4ab4093
to
1370469
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.
Did a second pass, everything looks reasonable!
} | ||
``` | ||
|
||
### The salsa-event mechanism will move to dynamic dispatch |
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.
At the moment, rust-analyzer uses this API extensively, as we use it to inject cancellation. We need to call event handler for every recomputation and revalidation event.
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'll just have to measure whether using a dyn interface here makes a perf difference, but I don't really see much of an alternative.
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 cancellation specifically, I think we should just build it into salsa.
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.
Yeah, that makes sense
immutable, including the list of dependencies. This in turn means that those | ||
fields can be traversed during revalidation without acquiring any read-locks. | ||
|
||
There is one complication, though. It sometimes happens that we a query Q has a |
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.
There is one complication, though. It sometimes happens that we a query Q has a | |
There is one complication, though. It sometimes happens that a query Q has a |
What I usually do is just ask salsa to dump all the code generated for hello world example. It might make sense to write this as a test (piping the output through |
I started playing around with implementation (you can see the commits in this branch) and I realized I may want to change one aspect of the current proposal. Instead of the struct DatabaseKeyIndex {
group_index: u16,
query_index: u16,
key_index: u32,
} This was actually my original idea, but I changed it to a centralized index at some point. This "3 integer" approach means that we can create fresh keys without any central locking or coordination. It works just as well for the main job of dispatching revalidation requests -- perhaps better. It also works particularly well with the "interning"-style queries, as the I think that for the the other kinds of queries, we would basically use a |
383fffd
to
0706be7
Compare
Yeah this is why I wrote up those "plumbing" chapters in the book, so I didn't have to keep doing that. I think there's a way to get a good diagram, but I didn't want to invest too much energy into it until we had worked out the changes due to this RFC. Note that the latest pushes implement the "triple index" scheme I described and also handle the tracking of dependencies via indices instead of direct references to slots. I think it worked out quite cleanly. |
Update: I think this RFC implies that the salsa::requires feature must be removed, as described in the latest commit. |
This comment has been minimized.
This comment has been minimized.
05ddf29
to
ad635dc
Compare
OK, I realized the solution to my dilemma and pushed an implementation of it. You now define databases via |
(I suppose it would have been a less invasive change to split out |
eece1c1
to
a4bca65
Compare
OK, I've basically implemented this RFC now, but I hit one final obstacle I hadn't anticipated: In the absence of GATs, the structure that I've setup here, lacking GATs, basically makes it impossible for databases to carry non-static borrows. The problem stems from one of the nicest simplifications in this RFC. Instead of having everything generic over This is annoying because the parameter is If we had GATs, we could make it That said, right now, database traits don't really support generic parameters anyway, so we're not actually losing anything afaik by requiring |
cf7bfcf
to
4b70a10
Compare
OK, fully rebased, tests pass, and the hardest part is implemented. I updated the OP with details of work left to do. |
Update book/src/rfcs/RFC0006-Dynamic-Databases.md Co-authored-by: Aleksey Kladov <[email protected]> Update book/src/rfcs/RFC0001-Query-Group-Traits.md Co-authored-by: bjorn3 <[email protected]> Update book/src/rfcs/RFC0006-Dynamic-Databases.md Co-authored-by: Aleksey Kladov <[email protected]> fix lint warnings on RFC
This will be more compatible once we move to having queries have an associated `DynDb` type. It also reads nicely.
This had two unexpected consequences, one unfortunate, one "medium": * All `salsa::Database` must be `'static`. This falls out from `Q::DynDb` not having access to any lifetimes, but also the defaulting rules for `dyn QueryGroup` that make it `dyn QueryGroup + 'static`. We don't really support generic databases anyway yet so this isn't a big deal, and we can add workarounds later (ideally via GATs). * It is now statically impossible to invoke `snapshot` from a query, and so we don't need to test that it panics. This is because the signature of `snapshot` returns a `Snapshot<Self>` and that is not accessible to a `dyn QueryGroup` type. Similarly, invoking `Runtime::snapshot` directly is not possible becaues it is crate-private. So I removed the test. This seems ok, but eventually I would like to expose ways for queries to do parallel execution (matklad and I had talked about a "speculation" primitive for enabling that). * This commit is 99% boilerplate I did with search-and-replace. I also rolled in a few other changes I might have preferred to factor out, most notably removing the `GetQueryTable` plumbing trait in favor of free-methods, but it was awkward to factor them out and get all the generics right (so much simpler in this version).
4b70a10
to
fad97ee
Compare
a50273d
to
0a8c203
Compare
It's simpler to just store a DatabaseKeyIndex. It may be somewhat slower, we'll have to measure. But we can add back in this other design later if we want.
This should enable more sharing and less monomorphization. There is probably room for more radical restructing in this vein.
BTW, I adjusted the docs, so if you get a chance, do a checkout and run |
Some doctests fail |
See rust-lang/rust-analyzer#1987 (comment) for visualization of impact on compile times. |
OK, doc-tests are fixed. I think we're going to merge this once CI is happy. |
The single (but big) change is Dynamic Database RFC implementation: #231
This PR introduces an RFC describing a shift to
dyn
-capable databases. The primary goal is to make it so that query-group code can be compiled in the query-group crate, rather than waiting to be monomorphized in the final database crate.The main user-visible effect of this proposal is that it requires all salsa query group traits to be dyn-safe.
Rendered form of the RFC
Current status
Largely implemented.
Pending work:
Pending updates the RFC text itself:
'static
limitation on databases and how it might be overcomedb.query(Q)
methods entirely in favor ofQ.in_db(&db)
. This does not require an extension trait and also works better withdyn Db
coercions.salsa::Database
andHasQueryGroup<G>
as an automatic supertrait to each query-group trait.