node: Update inventory when unseeding

Previously, our advertized inventory was not always correct: when we
stopped seeding a repository, it was still advertised as part of our
inventory until node restart, because the `Storage` type doesn't have
access to seeding policies.

In this change, we remove the concept of inventory from `Storage` and
make the authoritative place for it the routing table in our database.

To make this work, we have to add our node to the database on profile
creation, to not violate the foreign key constraint on the routing
table. Hence, the tests are changed to include our alias which is now
always available.
This commit is contained in:
cloudhead 2024-06-06 17:07:51 +02:00
parent d9fa83f9da
commit b24f8a30d6
No known key found for this signature in database
30 changed files with 510 additions and 432 deletions

View File

@ -7,7 +7,7 @@ $ rad issue open --title "flux capacitor underpowered" --description "Flux capac
╭─────────────────────────────────────────────────────────╮
│ Title flux capacitor underpowered │
│ Issue d87dcfe8c2b3200e78b128d9b959cfdf7063fefe │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Status open │
│ │
│ Flux capacitor power requirements exceed current supply │
@ -18,11 +18,11 @@ The issue is now listed under our project.
```
$ rad issue list
╭───────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Labels Assignees Opened │
├───────────────────────────────────────────────────────────────────────────────────────────────────
│ ● d87dcfe flux capacitor underpowered z6MknSL…StBU8Vi (you) now │
╰───────────────────────────────────────────────────────────────────────────────────────────────────
╭──────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Labels Assignees Opened │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ ● d87dcfe flux capacitor underpowered alice (you) now │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
```
Let's create a patch, too.
@ -42,11 +42,11 @@ Patch can be listed.
```
$ rad patch
╭──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● aa45913 Define power requirements z6MknSL…StBU8Vi (you) - 3e674d1 +0 -0 now │
╰──────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ ● aa45913 Define power requirements alice (you) - 3e674d1 +0 -0 now │
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```
Both issue and patch COBs can be listed.

View File

@ -10,7 +10,7 @@ $ rad issue open --title "spice harvester broken" --description "Fremen have att
╭──────────────────────────────────────────────────╮
│ Title spice harvester broken │
│ Issue 9de644864342d7a505eb8d58d1ef20e5bb05de2e │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Status open │
│ │
│ Fremen have attacked, maybe we went too far? │
@ -21,11 +21,11 @@ The issue is now listed under our project.
```
$ rad issue list
╭──────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Labels Assignees Opened │
├──────────────────────────────────────────────────────────────────────────────────────────────
│ ● 9de6448 spice harvester broken z6MknSL…StBU8Vi (you) now │
╰──────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Labels Assignees Opened │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ ● 9de6448 spice harvester broken alice (you) now │
╰─────────────────────────────────────────────────────────────────────────────────────╯
```
Let's create a patch, too.
@ -45,11 +45,11 @@ Patch can be listed.
```
$ rad patch
╭────────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├────────────────────────────────────────────────────────────────────────────────────────────────────
│ ● d1f7f86 Start drafting peace treaty z6MknSL…StBU8Vi (you) - 575ed68 +0 -0 now │
╰────────────────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├───────────────────────────────────────────────────────────────────────────────────────────┤
│ ● d1f7f86 Start drafting peace treaty alice (you) - 575ed68 +0 -0 now │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
```
Both issue and patch COBs can be listed.

View File

@ -3,18 +3,18 @@ Let's add a payload field and then delete it.
```
$ rad id update --title "Add field" --description "Add a new 'web' field" --payload xyz.radicle.project web '"https://acme.example"'
✓ Identity revision a8a9fee6c4f83578ab132d375f1da0c81282bef3 created
╭───────────────────────────────────────────────────────────────────╮
│ Title Add field │
│ Revision a8a9fee6c4f83578ab132d375f1da0c81282bef3 │
│ Blob fbe268d13e60f1f3a1972e0ccd592f3cdecf08b5 │
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
│ State accepted │
│ Quorum yes │
│ │
│ Add a new 'web' field │
├───────────────────────────────────────────────────────────────────┤
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) │
╰───────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────
│ Title Add field
│ Revision a8a9fee6c4f83578ab132d375f1da0c81282bef3
│ Blob fbe268d13e60f1f3a1972e0ccd592f3cdecf08b5
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
│ State accepted
│ Quorum yes
│ Add a new 'web' field
├────────────────────────────────────────────────────────────────────────
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
╰────────────────────────────────────────────────────────────────────────
@@ -1,13 +1,14 @@
{
@ -39,18 +39,18 @@ Now let's delete it by setting it to `null`.
```
$ rad id update --title "Delete field" --description "Delete 'web'" --payload xyz.radicle.project web null
✓ Identity revision d373c35876833105f8aafed8b610660b5737cd67 created
╭───────────────────────────────────────────────────────────────────╮
│ Title Delete field │
│ Revision d373c35876833105f8aafed8b610660b5737cd67 │
│ Blob d96f425412c9f8ad5d9a9a05c9831d0728e2338d │
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
│ State accepted │
│ Quorum yes │
│ │
│ Delete 'web' │
├───────────────────────────────────────────────────────────────────┤
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) │
╰───────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────
│ Title Delete field
│ Revision d373c35876833105f8aafed8b610660b5737cd67
│ Blob d96f425412c9f8ad5d9a9a05c9831d0728e2338d
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
│ State accepted
│ Quorum yes
│ Delete 'web'
├────────────────────────────────────────────────────────────────────────
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
╰────────────────────────────────────────────────────────────────────────
@@ -1,14 +1,13 @@
{

View File

@ -48,7 +48,7 @@ $ rad inspect --payload
}
}
$ rad inspect --delegates
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
```
Finally, the `--history` flag allows you to examine the identity document's

View File

@ -8,7 +8,7 @@ $ rad issue open --title "flux capacitor underpowered" --description "Flux capac
╭─────────────────────────────────────────────────────────╮
│ Title flux capacitor underpowered │
│ Issue d87dcfe8c2b3200e78b128d9b959cfdf7063fefe │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Status open │
│ │
│ Flux capacitor power requirements exceed current supply │
@ -19,11 +19,11 @@ The issue is now listed under our project.
```
$ rad issue list
╭───────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Labels Assignees Opened │
├───────────────────────────────────────────────────────────────────────────────────────────────────
│ ● d87dcfe flux capacitor underpowered z6MknSL…StBU8Vi (you) now │
╰───────────────────────────────────────────────────────────────────────────────────────────────────
╭──────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Labels Assignees Opened │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ ● d87dcfe flux capacitor underpowered alice (you) now │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
```
Show the issue information issue.
@ -33,7 +33,7 @@ $ rad issue show d87dcfe
╭─────────────────────────────────────────────────────────╮
│ Title flux capacitor underpowered │
│ Issue d87dcfe8c2b3200e78b128d9b959cfdf7063fefe │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Status open │
│ │
│ Flux capacitor power requirements exceed current supply │
@ -59,11 +59,11 @@ It will now show in the list of issues assigned to us, along with the new label.
```
$ rad issue list --assigned
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Labels Assignees Opened │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ ● d87dcfe flux capacitor underpowered z6MknSL…StBU8Vi (you) good-first-issue z6MknSL…StBU8Vi now │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Labels Assignees Opened │
├────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ● d87dcfe flux capacitor underpowered alice (you) good-first-issue alice now │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
```
Note: this can always be undone with the `unassign` subcommand.
@ -91,16 +91,16 @@ $ rad issue show d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
╭─────────────────────────────────────────────────────────╮
│ Title flux capacitor underpowered │
│ Issue d87dcfe8c2b3200e78b128d9b959cfdf7063fefe │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Labels good-first-issue │
│ Status open │
│ │
│ Flux capacitor power requirements exceed current supply │
├─────────────────────────────────────────────────────────┤
z6MknSL…StBU8Vi (you) now 2193e87
alice (you) now 2193e87
│ The flux capacitor needs 1.21 Gigawatts │
├─────────────────────────────────────────────────────────┤
z6MknSL…StBU8Vi (you) now 880fdcd
alice (you) now 880fdcd
│ More power! │
╰─────────────────────────────────────────────────────────╯
```

View File

@ -34,9 +34,9 @@ $ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias Bo
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (Bob)
```
Now, when we use the `rad seed` command we will see
information for repositories that we seed -- in this case a
repository that was already created:
Now, when we use the `rad seed` command we will see information for
repositories that we seed -- in this case a repository that was already
created:
```
$ rad seed
@ -87,6 +87,35 @@ $ rad node stop
✗ Stopping node... error: node is not running
```
Note that if we unseed a repository, it is no longer part of our inventory:
```
$ rad unseed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
✓ Seeding policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji removed
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```
Likewise, if we seed a repository we don't have locally, it won't show up as
part of our inventory:
```
$ rad seed rad:z3trNYnLWS11cJWC6BbxDs5niGo82
[...]
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
```
But if we start seeding the repository we have locally again, it'll show
up in our inventory:
```
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
[...]
$ rad node routing --nid z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
╭─────────────────────────────────────────────────────╮
│ RID NID │
├─────────────────────────────────────────────────────┤
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6MknSL…StBU8Vi │
╰─────────────────────────────────────────────────────╯
```
Some commands also give us a hint if the node isn't running:
``` (fail)

View File

@ -45,11 +45,11 @@ To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkE
When listing, we see that it has one addition:
```
$ rad patch list
╭─────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├─────────────────────────────────────────────────────────────────────────────────
│ ● 217f050 Add Alan z6MknSL…StBU8Vi (you) - 5c88a79 +1 -0 now │
╰─────────────────────────────────────────────────────────────────────────────────
╭────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├────────────────────────────────────────────────────────────────────────┤
│ ● 217f050 Add Alan alice (you) - 5c88a79 +1 -0 now │
╰────────────────────────────────────────────────────────────────────────╯
```
When showing the patch, we see that it is `ahead 1, behind 1`, since master has
@ -59,7 +59,7 @@ $ rad patch show -v -p 217f050
╭────────────────────────────────────────────────────╮
│ Title Add Alan │
│ Patch 217f050f8891def8fb863f7c0b4f85c89f97299d │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 │
│ Base f64fb2c8fe28f7c458c72ec8d700373924794943 │
│ Branches feature/1 │
@ -68,7 +68,7 @@ $ rad patch show -v -p 217f050
├────────────────────────────────────────────────────┤
│ 5c88a79 Add Alan │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (5c88a79) now
│ ● opened by alice (you) (5c88a79) now
╰────────────────────────────────────────────────────╯
commit 5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7
@ -105,7 +105,7 @@ $ rad patch show -v e22ff008e2a0ed47262890d13263031d7555b555
╭────────────────────────────────────────────────────╮
│ Title Add Mel │
│ Patch e22ff008e2a0ed47262890d13263031d7555b555 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 7f63fcbcf23fc39eea784c091ad3d20d7e4bd005 │
│ Base f64fb2c8fe28f7c458c72ec8d700373924794943 │
│ Branches feature/2 │
@ -115,7 +115,7 @@ $ rad patch show -v e22ff008e2a0ed47262890d13263031d7555b555
│ 7f63fcb Add Mel │
│ 5c88a79 Add Alan │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (7f63fcb) now
│ ● opened by alice (you) (7f63fcb) now
╰────────────────────────────────────────────────────╯
```
@ -140,7 +140,7 @@ $ rad patch show -v a467ffa260c4fbe355b6fb550ba0c4956078717e
╭────────────────────────────────────────────────────╮
│ Title Add Mel #2
│ Patch a467ffa260c4fbe355b6fb550ba0c4956078717e │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 7f63fcbcf23fc39eea784c091ad3d20d7e4bd005 │
│ Base 5c88a79d75f5c2b4cc51ee6f163d2db91ee198d7 │
│ Branches feature/2 │
@ -149,6 +149,6 @@ $ rad patch show -v a467ffa260c4fbe355b6fb550ba0c4956078717e
├────────────────────────────────────────────────────┤
│ 7f63fcb Add Mel │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (7f63fcb) now
│ ● opened by alice (you) (7f63fcb) now
╰────────────────────────────────────────────────────╯
```

View File

@ -47,7 +47,7 @@ $ rad patch show 183d343ab47d7fe18baf1b24b7209ad033d7fe5c -v
╭────────────────────────────────────────────────────╮
│ Title Add README, just for the fun │
│ Patch 183d343ab47d7fe18baf1b24b7209ad033d7fe5c │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
│ Base f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 │
│ Branches add-readme │
@ -57,7 +57,7 @@ $ rad patch show 183d343ab47d7fe18baf1b24b7209ad033d7fe5c -v
│ 27857ec Add README, just for the fun │
│ 3e674d1 Define power requirements │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (27857ec) now
│ ● opened by alice (you) (27857ec) now
╰────────────────────────────────────────────────────╯
```
@ -78,7 +78,7 @@ $ rad patch show 183d343 -v
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add README, just for the fun │
│ Patch 183d343ab47d7fe18baf1b24b7209ad033d7fe5c │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
│ Base 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
│ Branches add-readme │
@ -87,7 +87,7 @@ $ rad patch show 183d343 -v
├─────────────────────────────────────────────────────────────────────┤
│ 27857ec Add README, just for the fun │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (27857ec) now
│ ● opened by alice (you) (27857ec) now
│ ↑ updated to ebe76f9c2148eb595d7a745f82275786bf3458c3 (27857ec) now │
╰─────────────────────────────────────────────────────────────────────╯
```

View File

@ -18,7 +18,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title Define power requirements │
│ Patch aa45913e757cacd46972733bddee5472c78fa32a │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 639f44a25145a37f747f3c84265037a9461e44c5 │
│ Branches patch/aa45913 │
│ Commits ahead 3, behind 0 │
@ -30,7 +30,7 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun │
│ 3e674d1 Define power requirements │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now
│ ● opened by alice (you) (3e674d1) now
│ ↑ updated to 3156bed9d64d4675d6cf56612d217fc5f4e8a53a (27857ec) now │
│ ↑ updated to 2f5324f61e05cda65b667eeea02570d077a8e724 (639f44a) now │
╰─────────────────────────────────────────────────────────────────────╯

View File

@ -21,7 +21,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title Nothing yet │
│ Patch 97e18f8598237a396a1c0ac1509c89028e666c97 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches cloudhead/draft │
│ Commits ahead 1, behind 0 │
@ -29,7 +29,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here.. │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ]
│ ● opened by alice (you) (2a46583) [ .. ]
╰────────────────────────────────────────────────────╯
```
@ -44,7 +44,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title Nothing yet │
│ Patch 97e18f8598237a396a1c0ac1509c89028e666c97 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches cloudhead/draft │
│ Commits ahead 1, behind 0 │
@ -52,7 +52,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here.. │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ]
│ ● opened by alice (you) (2a46583) [ .. ]
╰────────────────────────────────────────────────────╯
```
@ -65,7 +65,7 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
╭────────────────────────────────────────────────────╮
│ Title Nothing yet │
│ Patch 97e18f8598237a396a1c0ac1509c89028e666c97 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 2a465832b5a76abe25be44a3a5d224bbd7741ba7 │
│ Branches cloudhead/draft │
│ Commits ahead 1, behind 0 │
@ -73,6 +73,6 @@ $ rad patch show 97e18f8598237a396a1c0ac1509c89028e666c97
├────────────────────────────────────────────────────┤
│ 2a46583 Nothing to see here.. │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (2a46583) [ .. ]
│ ● opened by alice (you) (2a46583) [ .. ]
╰────────────────────────────────────────────────────╯
```

View File

@ -44,7 +44,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add README, just for the fun │
│ Patch 89f7afb1511b976482b21f6b2f39aef7f4fb88a2 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 8945f6189adf027892c85ac57f7e9341049c2537 │
│ Branches changes │
│ Commits ahead 2, behind 0 │
@ -53,7 +53,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE │
│ 03c02af Add README, just for the fun │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now
│ ● opened by alice (you) (03c02af) now
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@ -67,7 +67,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add Metadata │
│ Patch 89f7afb1511b976482b21f6b2f39aef7f4fb88a2 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 8945f6189adf027892c85ac57f7e9341049c2537 │
│ Branches changes │
│ Commits ahead 2, behind 0 │
@ -78,7 +78,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE │
│ 03c02af Add README, just for the fun │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now
│ ● opened by alice (you) (03c02af) now
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@ -95,7 +95,7 @@ $ rad patch show 89f7afb
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add Metadata │
│ Patch 89f7afb1511b976482b21f6b2f39aef7f4fb88a2 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 8945f6189adf027892c85ac57f7e9341049c2537 │
│ Branches changes │
│ Commits ahead 2, behind 0 │
@ -106,7 +106,7 @@ $ rad patch show 89f7afb
│ 8945f61 Define the LICENSE │
│ 03c02af Add README, just for the fun │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (03c02af) now
│ ● opened by alice (you) (03c02af) now
│ ↑ updated to 5d78dd5376453e25df5988ec86951c99cb73742c (8945f61) now │
╰─────────────────────────────────────────────────────────────────────╯
```

View File

@ -16,7 +16,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
╭────────────────────────────────────────────────────╮
│ Title Not a real change │
│ Patch b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 51b2f0f77b9849bfaa3e9d3ff68ee2f57771d20c │
│ Branches feature/1 │
│ Commits ahead 1, behind 0 │
@ -24,7 +24,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
├────────────────────────────────────────────────────┤
│ 51b2f0f Not a real change │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (51b2f0f) now
│ ● opened by alice (you) (51b2f0f) now
╰────────────────────────────────────────────────────╯
```
@ -57,7 +57,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
╭─────────────────────────────────────────────────────────────────────╮
│ Title Not a real change │
│ Patch b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 4d272148458a17620541555b1f0905c01658aa9f │
│ Branches feature/1 │
│ Commits ahead 2, behind 0 │
@ -66,7 +66,7 @@ $ rad patch show b6a23eb08656de0ef1fcc0b5fe8820841e5cb2e5
│ 4d27214 Rename readme file │
│ 51b2f0f Not a real change │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (51b2f0f) now
│ ● opened by alice (you) (51b2f0f) now
│ ↑ updated to ea7def3857f62f404606d7cd6490cd0de4eaebd1 (4d27214) now │
╰─────────────────────────────────────────────────────────────────────╯
```

View File

@ -23,7 +23,7 @@ $ rad patch show 6035d2f582afbe01ff23ea87528ae523d76875b6
╭────────────────────────────────────────────────────╮
│ Title Add things #1
│ Patch 6035d2f582afbe01ff23ea87528ae523d76875b6 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 42d894a83c9c356552a57af09ccdbd5587a99045 │
│ Branches feature/1 │
│ Commits ahead 1, behind 0 │
@ -33,7 +33,7 @@ $ rad patch show 6035d2f582afbe01ff23ea87528ae523d76875b6
├────────────────────────────────────────────────────┤
│ 42d894a Add things │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (42d894a) now
│ ● opened by alice (you) (42d894a) now
╰────────────────────────────────────────────────────╯
```
@ -96,12 +96,12 @@ And both patches:
```
$ rad patch
╭────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├────────────────────────────────────────────────────────────────────────────────────────
│ ● 6035d2f Add things #1 z6MknSL…StBU8Vi (you) - 42d894a +0 -0 now │
│ ● 9580891 Add more things z6MknSL…StBU8Vi (you) - 8b0ea80 +0 -0 now │
╰────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├───────────────────────────────────────────────────────────────────────────────┤
│ ● 6035d2f Add things #1 alice (you) - 42d894a +0 -0 now │
│ ● 9580891 Add more things alice (you) - 8b0ea80 +0 -0 now │
╰───────────────────────────────────────────────────────────────────────────────╯
```
To update our patch, we simply push commits to the upstream branch:
@ -136,7 +136,7 @@ $ rad patch show 9580891
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add more things │
│ Patch 95808913573cead52ad7b42c7b475260ec45c4b2 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 02bef3fac41b2f98bb3c02b868a53ddfecb55b5f │
│ Branches feature/2 │
│ Commits ahead 2, behind 0 │
@ -145,7 +145,7 @@ $ rad patch show 9580891
│ 02bef3f Improve code │
│ 8b0ea80 Add more things │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (8b0ea80) now
│ ● opened by alice (you) (8b0ea80) now
│ ↑ updated to d7040c6c97629c2b94f86fb639bebbff5de39697 (02bef3f) now │
╰─────────────────────────────────────────────────────────────────────╯
```
@ -212,7 +212,7 @@ $ rad patch show 9580891
╭─────────────────────────────────────────────────────────────────────╮
│ Title Add more things │
│ Patch 95808913573cead52ad7b42c7b475260ec45c4b2 │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 9304dbc445925187994a7a93222a3f8bde73b785 │
│ Branches feature/2 │
│ Commits ahead 2, behind 0 │
@ -221,7 +221,7 @@ $ rad patch show 9580891
│ 9304dbc Amended commit │
│ 8b0ea80 Add more things │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (8b0ea80) now
│ ● opened by alice (you) (8b0ea80) now
│ ↑ updated to d7040c6c97629c2b94f86fb639bebbff5de39697 (02bef3f) now │
│ ↑ updated to 670d02794aa05afd6e0851f4aa848bc87c4712c7 (9304dbc) now │
╰─────────────────────────────────────────────────────────────────────╯

View File

@ -35,18 +35,18 @@ It will now be listed as one of the project's open patches.
```
$ rad patch
╭──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● aa45913 Define power requirements z6MknSL…StBU8Vi (you) - 3e674d1 +0 -0 now │
╰──────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ ● aa45913 Define power requirements alice (you) - 3e674d1 +0 -0 now │
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```
```
$ rad patch show aa45913e757cacd46972733bddee5472c78fa32a -p
╭────────────────────────────────────────────────────╮
│ Title Define power requirements │
│ Patch aa45913e757cacd46972733bddee5472c78fa32a │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Head 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
│ Branches flux-capacitor-power │
│ Commits ahead 1, behind 0 │
@ -56,7 +56,7 @@ $ rad patch show aa45913e757cacd46972733bddee5472c78fa32a -p
├────────────────────────────────────────────────────┤
│ 3e674d1 Define power requirements │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now
│ ● opened by alice (you) (3e674d1) now
╰────────────────────────────────────────────────────╯
commit 3e674d1a1df90807e934f9ae5da2591dd6848a33
@ -75,11 +75,11 @@ We can also list only patches that we've authored.
```
$ rad patch list --authored
╭──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● aa45913 Define power requirements z6MknSL…StBU8Vi (you) - 3e674d1 +0 -0 now │
╰──────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ ● aa45913 Define power requirements alice (you) - 3e674d1 +0 -0 now │
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```
We can also see that it set an upstream for our patch branch:
@ -99,7 +99,7 @@ $ rad patch show aa45913
╭────────────────────────────────────────────────────╮
│ Title Define power requirements │
│ Patch aa45913e757cacd46972733bddee5472c78fa32a │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Labels fun │
│ Head 3e674d1a1df90807e934f9ae5da2591dd6848a33 │
│ Branches flux-capacitor-power │
@ -110,7 +110,7 @@ $ rad patch show aa45913
├────────────────────────────────────────────────────┤
│ 3e674d1 Define power requirements │
├────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now
│ ● opened by alice (you) (3e674d1) now
╰────────────────────────────────────────────────────╯
```
@ -136,7 +136,7 @@ And let's leave a quick comment for our team:
```
$ rad patch comment aa45913 --message 'I cannot wait to get back to the 90s!' --no-announce
╭───────────────────────────────────────╮
z6MknSL…StBU8Vi (you) now 686ec1c
alice (you) now 686ec1c
│ I cannot wait to get back to the 90s! │
╰───────────────────────────────────────╯
$ rad patch comment aa45913 --message 'My favorite decade!' --reply-to 686ec1c -q --no-announce
@ -165,7 +165,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title Define power requirements │
│ Patch aa45913e757cacd46972733bddee5472c78fa32a │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Labels fun │
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
│ Branches flux-capacitor-power, patch/aa45913 │
@ -177,16 +177,16 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun │
│ 3e674d1 Define power requirements │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now
│ ● opened by alice (you) (3e674d1) now
│ ↑ updated to 6e5a3b7b2ce27b32e7ccc2f0b3f4594897dde638 (27857ec) now │
│ └─ ✓ accepted by z6MknSL…StBU8Vi (you) now
│ └─ ✓ accepted by alice (you) now
╰─────────────────────────────────────────────────────────────────────╯
$ rad patch list
╭──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● ID Title Author Reviews Head + - Updated │
├──────────────────────────────────────────────────────────────────────────────────────────────────
│ ● aa45913 Define power requirements z6MknSL…StBU8Vi (you) ✔ 27857ec +0 -0 now │
╰──────────────────────────────────────────────────────────────────────────────────────────────────
╭─────────────────────────────────────────────────────────────────────────────────────────╮
│ ● ID Title Author Reviews Head + - Updated │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ ● aa45913 Define power requirements alice (you) ✔ 27857ec +0 -0 now │
╰─────────────────────────────────────────────────────────────────────────────────────────╯
```
If you make a mistake on the patch description, you can always change it!
@ -197,7 +197,7 @@ $ rad patch show aa45913
╭─────────────────────────────────────────────────────────────────────╮
│ Title Define power requirements │
│ Patch aa45913e757cacd46972733bddee5472c78fa32a │
│ Author z6MknSL…StBU8Vi (you)
│ Author alice (you)
│ Labels fun │
│ Head 27857ec9eb04c69cacab516e8bf4b5fd36090f66 │
│ Branches flux-capacitor-power, patch/aa45913 │
@ -209,8 +209,8 @@ $ rad patch show aa45913
│ 27857ec Add README, just for the fun │
│ 3e674d1 Define power requirements │
├─────────────────────────────────────────────────────────────────────┤
│ ● opened by z6MknSL…StBU8Vi (you) (3e674d1) now
│ ● opened by alice (you) (3e674d1) now
│ ↑ updated to 6e5a3b7b2ce27b32e7ccc2f0b3f4594897dde638 (27857ec) now │
│ └─ ✓ accepted by z6MknSL…StBU8Vi (you) now
│ └─ ✓ accepted by alice (you) now
╰─────────────────────────────────────────────────────────────────────╯
```

View File

@ -27,18 +27,18 @@ repository private again __will not_ be replicated.
```
$ rad id update --visibility private --title "Privatise" --description "Reverting the rad publish event"
✓ Identity revision 774cc1e72641d97d7dc9377745b7f454a9171747 created
╭───────────────────────────────────────────────────────────────────╮
│ Title Privatise │
│ Revision 774cc1e72641d97d7dc9377745b7f454a9171747 │
│ Blob 88f759a4d46e9535766fccec0cbfe1fed6160b1a │
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
│ State accepted │
│ Quorum yes │
│ │
│ Reverting the rad publish event │
├───────────────────────────────────────────────────────────────────┤
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (you) │
╰───────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────
│ Title Privatise
│ Revision 774cc1e72641d97d7dc9377745b7f454a9171747
│ Blob 88f759a4d46e9535766fccec0cbfe1fed6160b1a
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
│ State accepted
│ Quorum yes
│ Reverting the rad publish event
├────────────────────────────────────────────────────────────────────────
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
╰────────────────────────────────────────────────────────────────────────
@@ -1,13 +1,16 @@
{

View File

@ -287,11 +287,14 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
if options.verbose {
term::blob(json::to_string_pretty(&proj)?);
}
// It's important to seed our own repositories to make sure that our node signals
// interest for them. This ensures that messages relating to them are relayed to us.
if options.seed {
cli::project::seed(rid, options.scope, &mut node, profile)?;
if doc.visibility.is_public() {
cli::project::add_inventory(rid, &mut node, profile)?;
}
}
if options.set_upstream || git::branch_remote(&repo, proj.default_branch()).is_err() {
@ -370,7 +373,6 @@ fn sync(
let events = node.subscribe(DEFAULT_SUBSCRIBE_TIMEOUT)?;
let sessions = node.sessions()?;
node.update_inventory(rid)?;
spinner.message("Announcing..");
if !sessions.iter().any(|s| s.is_connected()) {

View File

@ -27,7 +27,7 @@ pub fn run<S: node::routing::Store>(
fn print_table(entries: impl IntoIterator<Item = (RepoId, NodeId)>) {
let mut t = term::Table::new(term::table::TableOptions::bordered());
t.push([
t.header([
term::format::default(String::from("RID")),
term::format::default(String::from("NID")),
]);

View File

@ -1,9 +1,10 @@
use localtime::LocalTime;
use radicle::prelude::*;
use crate::git;
use radicle::git::RefStr;
use radicle::node::policy::Scope;
use radicle::node::{Handle, NodeId};
use radicle::node::{routing, Handle, NodeId};
use radicle::Node;
/// Setup a repository remote and tracking branch.
@ -52,8 +53,28 @@ impl<'a> SetupRemote<'a> {
}
}
/// Seed a repository by first trying to seed through the node, and if the node isn't running,
/// by updating the policy database directly.
/// Add the repo to our inventory.
pub fn add_inventory(
rid: RepoId,
node: &mut Node,
profile: &Profile,
) -> Result<bool, anyhow::Error> {
match node.update_inventory(rid) {
Ok(updated) => Ok(updated),
Err(e) if e.is_connection_err() => {
let now = LocalTime::now();
let mut db = profile.database_mut()?;
let updates = routing::Store::insert(&mut db, [&rid], *profile.id(), now.into())?;
Ok(!updates.is_empty())
}
Err(e) => Err(e.into()),
}
}
/// Seed a repository by first trying to seed through the node, and if the node isn't running, by
/// updating the policy database directly. If the repo is available locally, we also add it to our
/// inventory.
pub fn seed(
rid: RepoId,
scope: Scope,
@ -64,7 +85,15 @@ pub fn seed(
Ok(updated) => Ok(updated),
Err(e) if e.is_connection_err() => {
let mut config = profile.policies_mut()?;
config.seed(&rid, scope).map_err(|e| e.into())
let result = config.seed(&rid, scope)?;
if result && profile.storage.contains(&rid)? {
let now = LocalTime::now();
let mut db = profile.database_mut()?;
routing::Store::insert(&mut db, [&rid], *profile.id(), now.into())?;
}
Ok(result)
}
Err(e) => Err(e.into()),
}
@ -77,7 +106,12 @@ pub fn unseed(rid: RepoId, node: &mut Node, profile: &Profile) -> Result<bool, a
Ok(updated) => Ok(updated),
Err(e) if e.is_connection_err() => {
let mut config = profile.policies_mut()?;
config.unseed(&rid).map_err(|e| e.into())
let result = config.unseed(&rid)?;
let mut db = profile.database_mut()?;
radicle::node::routing::Store::remove(&mut db, &rid, profile.id())?;
Ok(result)
}
Err(e) => Err(e.into()),
}

View File

@ -730,6 +730,7 @@ fn rad_node() {
Address::from(net::SocketAddr::from(([41, 12, 98, 112], 8776))),
Address::from_str("seed.cloudhead.io:8776").unwrap(),
],
seeding_policy: SeedingPolicy::Block,
..Config::test(Alias::new("alice"))
});
let working = tempfile::tempdir().unwrap();
@ -1286,6 +1287,8 @@ fn rad_clone_partial_fail() {
let working = environment.tmp().join("working");
let carol = NodeId::from_str("z6MksFqXN3Yhqk8pTJdUGLwBTkRfQvwZXPqR2qMEhbS9wzpT").unwrap();
logger::init(log::Level::Debug);
// Setup a test project.
let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
@ -1323,7 +1326,8 @@ fn rad_clone_partial_fail() {
eve.connect(&alice);
eve.connect(&bob);
eve.routes_to(&[(acme, carol), (acme, bob.id), (acme, alice.id)]);
bob.handle.unseed(acme).unwrap(); // Cause the fetch with bob to fail.
bob.storage.repository(acme).unwrap().remove().unwrap(); // Cause the fetch from Bob to fail.
bob.storage.lock_repository(acme).ok(); // Prevent repo from being re-fetched.
test(
"examples/rad-clone-partial-fail.md",

View File

@ -125,10 +125,6 @@ impl Runtime {
log::warn!(target: "node", "Unused or deprecated configuration attribute {:?}", key);
}
log::info!(target: "node", "Opening node database..");
let db = home
.database_mut()?
.journal_mode(node::db::JournalMode::default())?;
let mut stores: service::Stores<_> = db.clone().into();
log::info!(target: "node", "Opening policy database..");
let policies = home.policies_mut()?;
@ -175,6 +171,18 @@ impl Runtime {
.expect("Runtime::init: unable to solve proof-of-work puzzle")
};
let db = home
.database_mut()?
.journal_mode(node::db::JournalMode::default())?
.init(
&id,
announcement.features,
announcement.alias.clone(),
announcement.timestamp,
announcement.addresses.iter(),
)?;
let mut stores: service::Stores<_> = db.clone().into();
if config.connect.is_empty() && stores.addresses().is_empty()? {
log::info!(target: "node", "Address book is empty. Adding bootstrap nodes..");

View File

@ -10,7 +10,7 @@ pub mod message;
pub mod session;
use std::collections::hash_map::Entry;
use std::collections::{BTreeSet, HashMap, VecDeque};
use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
use std::net::IpAddr;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
@ -391,6 +391,12 @@ where
}
}
impl<D> AsMut<D> for Stores<D> {
fn as_mut(&mut self) -> &mut D {
&mut self.0
}
}
impl<D> From<D> for Stores<D> {
fn from(db: D) -> Self {
Self(db)
@ -555,16 +561,23 @@ where
/// simply not announcing it anymore, it will eventually be pruned by nodes.
pub fn unseed(&mut self, id: &RepoId) -> Result<bool, policy::Error> {
let updated = self.policies.unseed(id)?;
// Nb. This is potentially slow if we have lots of repos. We should probably
// only re-compute the filter when we've unseeded a certain amount of repos
// and the filter is really out of date.
//
// TODO: Share this code with initialization code.
self.filter = Filter::new(
self.policies
.seed_policies()?
.filter_map(|t| (t.policy.is_allow()).then_some(t.rid)),
);
if updated {
// Nb. This is potentially slow if we have lots of repos. We should probably
// only re-compute the filter when we've unseeded a certain amount of repos
// and the filter is really out of date.
//
// TODO: Share this code with initialization code.
self.filter = Filter::new(
self.policies
.seed_policies()?
.filter_map(|t| (t.policy.is_allow()).then_some(t.rid)),
);
// Update and announce new inventory.
if let Err(e) = self.remove_inventory(id) {
error!(target: "service", "Error updating inventory after unseed: {e}");
}
}
Ok(updated)
}
@ -623,12 +636,18 @@ where
/// Lookup a repository, both locally and in the routing table.
pub fn lookup(&self, rid: RepoId) -> Result<Lookup, LookupError> {
let remote = self.db.routing().get(&rid)?.iter().cloned().collect();
let this = self.nid();
let local = self.storage.get(rid)?;
let remote = self
.db
.routing()
.get(&rid)?
.iter()
.filter(|nid| nid != &this)
.cloned()
.collect();
Ok(Lookup {
local: self.storage.get(rid)?,
remote,
})
Ok(Lookup { local, remote })
}
/// Initialize service with current time. Call this once.
@ -662,27 +681,13 @@ where
Err(e) => error!(target: "service", "Error checking refs database: {e}"),
}
// Ensure that our local node is in our address database.
self.db
.addresses_mut()
.insert(
&nid,
self.node.features,
self.node.alias.clone(),
self.node.work(),
self.node.timestamp,
self.node
.addresses
.iter()
.map(|a| KnownAddress::new(a.clone(), address::Source::Peer)),
)
.expect("Service::initialize: error adding local node to address database");
let announced = self
.db
.seeds()
.seeded_by(&nid)?
.collect::<Result<HashMap<_, _>, _>>()?;
let mut inventory = BTreeSet::new();
for repo in self.storage.repositories()? {
let rid = repo.rid;
@ -693,7 +698,7 @@ where
}
// Add public repositories to inventory.
if repo.doc.visibility.is_public() {
self.storage.insert(rid);
inventory.insert(rid);
}
// If we have no owned refs for this repo, then there's nothing to announce.
let Some(updated_at) = repo.synced_at else {
@ -724,7 +729,6 @@ where
}
{
let inventory = self.storage.inventory()?;
// Ensure that our inventory is recorded in our routing table, and we are seeding
// all of it. It can happen that inventory is not properly seeded if for eg. the
// user creates a new repository while the node is stopped.
@ -805,7 +809,7 @@ where
if now - self.last_sync >= SYNC_INTERVAL {
trace!(target: "service", "Running 'sync' task...");
if let Err(e) = self.fetch_missing_inventory() {
if let Err(e) = self.fetch_missing_repositories() {
error!(target: "service", "Error fetching missing inventory: {e}");
}
self.outbox.wakeup(SYNC_INTERVAL);
@ -814,9 +818,7 @@ where
if now - self.last_announce >= ANNOUNCE_INTERVAL {
trace!(target: "service", "Running 'announce' task...");
if let Err(err) = self.announce_inventory() {
error!(target: "service", "Error announcing inventory: {err}");
}
self.announce_inventory();
self.outbox.wakeup(ANNOUNCE_INTERVAL);
}
if now - self.last_prune >= PRUNE_INTERVAL {
@ -939,19 +941,16 @@ where
}
}
Command::AnnounceInventory => {
if let Err(err) = self.announce_inventory() {
error!(target: "service", "Error announcing inventory: {err}");
self.announce_inventory();
}
Command::UpdateInventory(rid, resp) => match self.add_inventory(rid) {
Ok(updated) => {
resp.send(updated).ok();
}
}
Command::UpdateInventory(rid, resp) => {
self.storage.insert(rid);
let synced = self
.sync_inventory()
.expect("Service::command: error syncing inventory");
resp.send(synced.added.len() + synced.removed.len() > 0)
.ok();
}
Err(e) => {
error!(target: "service", "Error adding {rid} to inventory: {e}");
}
},
Command::QueryState(query, sender) => {
sender.send(query(self)).ok();
}
@ -1171,8 +1170,9 @@ where
if clone && doc.visibility.is_public() {
debug!(target: "service", "Updating and announcing inventory for cloned repository {rid}..");
self.storage.insert(rid);
self.sync_and_announce_inventory();
if let Err(e) = self.add_inventory(rid) {
error!(target: "service", "Error announcing inventory for {rid}: {e}");
}
}
// It's possible for a fetch to succeed but nothing was updated.
@ -1539,8 +1539,6 @@ where
return Ok(None);
}
}
let inventory = self.storage.inventory_ref();
let mut missing = Vec::new();
for id in message.inventory.as_slice() {
@ -1560,14 +1558,20 @@ where
) {
// Only if we do not have the repository locally do we fetch here.
// If we do have it, only fetch after receiving a ref announcement.
if !inventory.contains(id) {
missing.push(*id);
match self.db.routing().entry(id, self.nid()) {
Ok(entry) => {
if entry.is_none() {
missing.push(*id);
}
}
Err(e) => error!(
target: "service",
"Error checking local inventory for {id}: {e}"
),
}
}
}
}
drop(inventory);
for rid in missing {
debug!(target: "service", "Missing seeded inventory {rid}; initiating fetch..");
self.fetch(rid, *announcer, FETCH_TIMEOUT, None);
@ -1931,14 +1935,59 @@ where
> 0
}
/// Update our routing table with our local node's inventory.
fn sync_inventory(&mut self) -> Result<SyncedRouting, Error> {
let inventory = self.storage.inventory()?;
let result = self.sync_routing(inventory.clone(), self.node_id(), self.clock.into())?;
// Update cached inventory message.
self.inventory = gossip::inventory(self.timestamp(), inventory);
/// Remove a local repository from our inventory.
fn remove_inventory(&mut self, rid: &RepoId) -> Result<bool, Error> {
let node = self.node_id();
let now = self.timestamp();
Ok(result)
// Remove inventory.
let removed = self.db.routing_mut().remove(rid, &node)?;
if removed {
// Update cached inventory message.
let inventory = self.inventory()?;
self.inventory = gossip::inventory(now, inventory);
self.announce_inventory();
}
Ok(removed)
}
/// Add a local repository to our inventory.
fn add_inventory(&mut self, rid: RepoId) -> Result<bool, Error> {
let node = self.node_id();
let now = self.timestamp();
if !self.storage.contains(&rid)? {
error!(target: "service", "Attempt to add non-existing inventory {rid}: repository not found in storage");
return Ok(false);
}
// Add to our inventory.
let updates = self.db.routing_mut().insert([&rid], node, now)?;
let updated = !updates.is_empty();
if updated {
let inventory = self.inventory()?;
self.inventory = gossip::inventory(now, inventory);
self.announce_inventory();
}
Ok(updated)
}
/// Get our local inventory.
///
/// A node's inventory is the advertized list of repositories offered by a node.
///
/// A node's inventory consists of *public* repositories that are seeded and available locally
/// in the node's storage. We use the routing table as the canonical state of all inventories,
/// including the local node's.
///
/// When a repository is unseeded, it is also removed from the inventory. Private repositories
/// are *not* part of a node's inventory.
fn inventory(&self) -> Result<HashSet<RepoId>, Error> {
self.db
.routing()
.get_resources(self.nid())
.map_err(Error::from)
}
/// Process a peer inventory announcement by updating our routing table.
@ -2093,22 +2142,6 @@ where
Ok((refs, timestamp))
}
fn sync_and_announce_inventory(&mut self) {
match self.sync_inventory() {
Ok(synced) => {
// Only announce if our inventory changed.
if synced.added.len() + synced.removed.len() > 0 {
if let Err(e) = self.announce_inventory() {
error!(target: "service", "Failed to announce inventory: {e}");
}
}
}
Err(e) => {
error!(target: "service", "Failed to sync inventory: {e}");
}
}
}
fn reconnect(&mut self, nid: NodeId, addr: Address) -> bool {
if let Some(sess) = self.sessions.get_mut(&nid) {
sess.to_initial();
@ -2263,7 +2296,9 @@ where
}
/// Announce our inventory to all connected peers.
fn announce_inventory(&mut self) -> Result<(), storage::Error> {
fn announce_inventory(&mut self) {
// TODO: If we're going to announce soon, skip this.
// TODO: If we just announced this exact inventory, also skip.
let msg = AnnouncementMessage::from(self.inventory.clone());
self.outbox.announce(
@ -2272,8 +2307,6 @@ where
self.db.gossip_mut(),
);
self.last_announce = self.clock;
Ok(())
}
fn prune_routing_entries(&mut self, now: &LocalTime) -> Result<(), routing::Error> {
@ -2355,16 +2388,17 @@ where
}
}
/// Fetch all repositories that are seeded but missing from our inventory.
fn fetch_missing_inventory(&mut self) -> Result<(), Error> {
let inventory = self.storage().inventory()?;
let missing = self
.policies
.seed_policies()?
.filter_map(|t| (t.policy.is_allow()).then_some(t.rid))
.filter(|rid| !inventory.contains(rid));
/// Fetch all repositories that are seeded but missing from storage.
fn fetch_missing_repositories(&mut self) -> Result<(), Error> {
for policy in self.policies.seed_policies()? {
let rid = policy.rid;
for rid in missing {
if !policy.is_allow() {
continue;
}
if self.storage.contains(&rid)? {
continue;
}
match self.seeds(&rid) {
Ok(seeds) => {
if let Some(connected) = NonEmpty::from_vec(seeds.connected().collect()) {

View File

@ -9,6 +9,7 @@ use std::{
use crossbeam_channel as chan;
use localtime::LocalTime;
use radicle::cob::cache::COBS_DB_FILE;
use radicle::cob::issue;
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
@ -18,7 +19,6 @@ use radicle::git::refname;
use radicle::identity::{RepoId, Visibility};
use radicle::node::config::ConnectAddress;
use radicle::node::policy::store as policy;
use radicle::node::routing::Store;
use radicle::node::seed::Store as _;
use radicle::node::Database;
use radicle::node::{Alias, POLICIES_DB_FILE};
@ -113,21 +113,32 @@ impl Environment {
let keypair = KeyPair::from_seed(Seed::from([!(self.users as u8); 32]));
let policies_db = home.node().join(POLICIES_DB_FILE);
let cobs_db = home.cobs().join(COBS_DB_FILE);
let now = LocalTime::now();
config.write(&home.config()).unwrap();
let storage = Storage::open(
home.storage(),
git::UserInfo {
alias,
alias: alias.clone(),
key: keypair.pk.into(),
},
)
.unwrap();
let public_key = keypair.pk.into();
policy::Store::open(policies_db).unwrap();
home.database_mut().unwrap(); // Just create the database.
cob::cache::Store::open(cobs_db).unwrap();
home.database_mut()
.unwrap()
.init(
&public_key,
config.node.features(),
Alias::new(alias),
now.into(),
config.node.external_addresses.iter(),
)
.unwrap();
transport::local::register(storage.clone());
keystore.store(keypair.clone(), "radicle", None).unwrap();
@ -139,7 +150,7 @@ impl Environment {
home,
storage,
keystore,
public_key: keypair.pk.into(),
public_key,
config,
}
}
@ -248,10 +259,15 @@ impl<G: Signer + cyphernet::Ecdh> NodeHandle<G> {
/// Get routing table entries.
pub fn routing(&self) -> impl Iterator<Item = (RepoId, NodeId)> {
Database::reader(self.home.node().join(node::NODE_DB_FILE))
.unwrap()
.entries()
.unwrap()
use node::routing::Store as _;
self.home.routing_mut().unwrap().entries().unwrap()
}
pub fn inventory(&self) -> impl Iterator<Item = RepoId> + '_ {
self.routing()
.filter(|(_, n)| *n == self.id)
.map(|(r, _)| r)
}
/// Get sync status of a repo.
@ -605,7 +621,7 @@ pub fn converge<'a, G: Signer + cyphernet::Ecdh + 'static>(
all_routes.insert((rid, seed_id));
}
// Routes from the local inventory.
for rid in node.storage.inventory().unwrap() {
for rid in node.inventory() {
all_routes.insert((rid, node.id));
}
}

View File

@ -1,4 +1,5 @@
#![allow(dead_code)]
use std::collections::HashSet;
use std::iter;
use std::net;
use std::ops::{Deref, DerefMut};
@ -19,6 +20,7 @@ use crate::crypto::test::signer::MockSigner;
use crate::crypto::Signer;
use crate::identity::RepoId;
use crate::node;
use crate::node::routing::Store as _;
use crate::prelude::*;
use crate::runtime::Emitter;
use crate::service;
@ -27,7 +29,6 @@ use crate::service::message::*;
use crate::service::policy::{Scope, SeedingPolicy};
use crate::service::*;
use crate::storage::git::transport::remote;
use crate::storage::Inventory;
use crate::storage::{RemoteId, WriteStorage};
use crate::test::storage::MockStorage;
use crate::test::{arbitrary, fixtures, simulator};
@ -99,7 +100,6 @@ where
pub struct Config<G: Signer + 'static> {
pub config: service::Config,
pub db: Stores<node::Database>,
pub local_time: LocalTime,
pub policy: SeedingPolicy,
pub signer: G,
@ -112,13 +112,10 @@ impl Default for Config<MockSigner> {
let mut rng = fastrand::Rng::new();
let signer = MockSigner::new(&mut rng);
let tmp = tempfile::TempDir::new().unwrap();
let db = Database::open(tmp.path().join(node::NODE_DB_FILE))
.unwrap()
.into();
let config = service::Config::test(Alias::from_str("mocky").unwrap());
Config {
config: service::Config::test(Alias::from_str("mocky").unwrap()),
db,
config,
local_time: LocalTime::now(),
policy: SeedingPolicy::default(),
signer,
@ -164,19 +161,32 @@ where
let id = *config.signer.public_key();
let ip = ip.into();
let local_addr = net::SocketAddr::new(ip, config.rng.u16(..));
let inventory = storage.inventory().unwrap();
let inventory = storage.repositories().unwrap();
// Make sure the peer address is advertized.
config.config.external_addresses.push(local_addr.into());
for rid in &inventory {
policies.seed(rid, Scope::Followed).unwrap();
for repo in &inventory {
policies.seed(&repo.rid, Scope::Followed).unwrap();
}
// Initialize database.
let db = Database::open(config.tmp.path().join(node::NODE_DB_FILE))
.unwrap()
.init(
&id,
config.config.features(),
config.config.alias.clone(),
config.local_time.into(),
config.config.external_addresses.iter(),
)
.unwrap()
.into();
let announcement =
service::gossip::node(&config.config, Timestamp::from(config.local_time) + 1);
let emitter: Emitter<Event> = Default::default();
let service = Service::new(
config.config,
config.db,
db,
storage,
policies,
config.signer,
@ -255,8 +265,12 @@ where
(*self.clock()).into()
}
pub fn inventory(&self) -> Inventory {
self.service.storage().inventory().unwrap()
pub fn inventory(&self) -> HashSet<RepoId> {
self.service
.database()
.routing()
.get_resources(self.nid())
.unwrap()
}
pub fn git_url(&self, repo: RepoId, namespace: Option<RemoteId>) -> remote::Url {

View File

@ -274,21 +274,21 @@ fn test_inventory_sync() {
let bob_storage = fixtures::storage(tmp.path().join("bob"), &bob_signer).unwrap();
let bob = Peer::with_storage("bob", [8, 8, 8, 8], bob_storage);
let now = LocalTime::now().into();
let projs = bob.storage().inventory().unwrap();
let repos = bob.inventory().into_iter().collect::<Vec<_>>();
alice.connect_to(&bob);
alice.receive(
bob.id(),
Message::inventory(
InventoryAnnouncement {
inventory: projs.clone().try_into().unwrap(),
inventory: repos.clone().try_into().unwrap(),
timestamp: now,
},
bob.signer(),
),
);
for proj in &projs {
for proj in &repos {
let seeds = alice.database().routing().get(proj).unwrap();
assert!(seeds.contains(&bob.node_id()));
}
@ -701,12 +701,7 @@ fn test_refs_announcement_relay() {
)
.initialized()
};
let bob_inv = bob
.storage()
.inventory()
.unwrap()
.into_iter()
.collect::<Vec<_>>();
let bob_inv = bob.inventory().into_iter().collect::<Vec<_>>();
alice.seed(&bob_inv[0], policy::Scope::All).unwrap();
alice.seed(&bob_inv[1], policy::Scope::All).unwrap();
@ -779,8 +774,8 @@ fn test_refs_announcement_fetch_trusted_no_inventory() {
)
.initialized()
};
let bob_inv = bob.storage().inventory().unwrap();
let rid = bob_inv.first().unwrap();
let bob_inv = bob.inventory();
let rid = bob_inv.iter().next().unwrap();
alice.seed(rid, policy::Scope::Followed).unwrap();
alice.connect_to(&bob);
@ -887,10 +882,7 @@ fn test_refs_announcement_offline() {
},
)
};
let mut inv = alice.inventory();
let rid = *inv.first().unwrap();
let mut bob = Peer::new("bob", [8, 8, 8, 8]);
bob.seed(&rid, policy::Scope::All).unwrap();
// Make sure alice's service wasn't initialized before.
assert_eq!(*alice.clock(), LocalTime::default());
@ -899,6 +891,11 @@ fn test_refs_announcement_offline() {
alice.connect_to(&bob);
alice.receive(bob.id, Message::Subscribe(Subscribe::all()));
let mut inv = alice.inventory();
let rid = *inv.iter().next().unwrap();
bob.seed(&rid, policy::Scope::All).unwrap();
// Alice announces the refs of all projects since she hasn't announced refs for these projects
// yet.
for msg in alice.messages(bob.id()) {
@ -1429,7 +1426,7 @@ fn test_queued_fetch_max_capacity() {
#[test]
fn test_queued_fetch_from_ann_same_rid() {
let storage = arbitrary::nonempty_storage(3);
let storage = arbitrary::nonempty_storage(1); // We're testing both public and private repos.
let mut repo_keys = storage.repos.keys();
let rid = *repo_keys.next().unwrap();
let mut alice = Peer::with_storage("alice", [7, 7, 7, 7], storage);
@ -1448,8 +1445,6 @@ fn test_queued_fetch_from_ann_same_rid() {
timestamp: bob.timestamp(),
};
logger::init(log::Level::Trace);
alice.seed(&rid, policy::Scope::All).unwrap();
alice.connect_to(&bob);
alice.connect_to(&eve);
@ -1713,9 +1708,27 @@ fn test_init_and_seed() {
fn prop_inventory_exchange_dense() {
fn property(alice_inv: MockStorage, bob_inv: MockStorage, eve_inv: MockStorage) {
let rng = fastrand::Rng::new();
let alice = Peer::with_storage("alice", [7, 7, 7, 7], alice_inv.clone());
let mut bob = Peer::with_storage("bob", [8, 8, 8, 8], bob_inv.clone());
let mut eve = Peer::with_storage("eve", [9, 9, 9, 9], eve_inv.clone());
let alice = Peer::with_storage(
"alice",
[7, 7, 7, 7],
alice_inv
.clone()
.map(|doc| doc.visibility = Visibility::Public),
);
let mut bob = Peer::with_storage(
"bob",
[8, 8, 8, 8],
bob_inv
.clone()
.map(|doc| doc.visibility = Visibility::Public),
);
let mut eve = Peer::with_storage(
"eve",
[9, 9, 9, 9],
eve_inv
.clone()
.map(|doc| doc.visibility = Visibility::Public),
);
let mut routing = RandomMap::with_hasher(rng.clone().into());
for (inv, peer) in &[
@ -1913,7 +1926,6 @@ fn test_announcement_message_amplification() {
.repos
.insert(rid, gen::<MockRepository>(1));
alice.command(Command::UpdateInventory(rid, tx));
alice.command(Command::AnnounceInventory);
sim.run_while([&mut alice, &mut bob, &mut eve, &mut zod, &mut tom], |s| {
s.elapsed() < LocalDuration::from_mins(3)

View File

@ -15,6 +15,7 @@ use std::{fmt, time};
use sqlite as sql;
use thiserror::Error;
use crate::node::{address, Address, Alias, Features, KnownAddress, NodeId, Timestamp};
use crate::sql::transaction;
/// How long to wait for the database lock to be released before failing a read.
@ -34,6 +35,9 @@ const MIGRATIONS: &[&str] = &[
#[derive(Error, Debug)]
pub enum Error {
/// Initialization error.
#[error("error initializing the database: {0}")]
Init(#[from] address::store::Error),
/// An Internal error.
#[error("internal error: {0}")]
Internal(#[from] sql::Error),
@ -121,6 +125,30 @@ impl Database {
Ok(self)
}
/// Initialize by adding our local node to the database.
pub fn init<'a>(
mut self,
node: &NodeId,
features: Features,
alias: Alias,
timestamp: Timestamp,
addrs: impl IntoIterator<Item = &'a Address>,
) -> Result<Self, Error> {
address::Store::insert(
&mut self,
node,
features,
alias,
0,
timestamp,
addrs
.into_iter()
.map(|a| KnownAddress::new(a.clone(), address::Source::Imported)),
)?;
Ok(self)
}
/// Create a new in-memory database.
pub fn memory() -> Result<Self, Error> {
let db = sql::Connection::open_thread_safe(":memory:")?;

View File

@ -14,6 +14,7 @@ use std::io::Write;
use std::path::{Path, PathBuf};
use std::{fs, io};
use localtime::LocalTime;
use serde::Serialize;
use serde_json as json;
use thiserror::Error;
@ -307,8 +308,16 @@ impl Profile {
)?;
// Create DBs.
home.policies_mut()?;
home.database_mut()?;
home.notifications_mut()?;
home.database_mut()?
.journal_mode(node::db::JournalMode::default())?
.init(
&public_key,
config.node.features(),
config.node.alias.clone(),
LocalTime::now().into(),
config.node.external_addresses.iter(),
)?;
transport::local::register(storage.clone());
@ -576,6 +585,16 @@ impl Home {
Ok(db)
}
/// Returns the address store.
pub fn addresses(&self) -> Result<impl node::address::Store, node::db::Error> {
self.database_mut()
}
/// Returns the routing store.
pub fn routing_mut(&self) -> Result<impl node::routing::Store, node::db::Error> {
self.database_mut()
}
/// Return a read-only handle for the issues cache.
pub fn issues<'a, R>(
&self,

View File

@ -73,9 +73,6 @@ pub fn init<G: Signer, S: WriteStorage>(
let (project, _) = Repository::init(&doc, &storage, signer)?;
let url = git::Url::from(project.id);
// Update inventory cache for this storage instance.
storage.insert(project.id);
match init_configure(repo, &project, pk, &default_branch, &url, signer) {
Ok(signed) => Ok((project.id, doc, signed)),
Err(err) => {

View File

@ -1,7 +1,7 @@
pub mod git;
pub mod refs;
use std::collections::{hash_map, BTreeSet, HashSet};
use std::collections::{hash_map, HashSet};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::{fmt, io};
@ -29,7 +29,6 @@ use self::git::UserInfo;
use self::refs::{RefsAt, SignedRefs};
pub type BranchName = git::RefString;
pub type Inventory = BTreeSet<RepoId>;
/// Basic repository information.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -150,8 +149,6 @@ pub enum Error {
Ext(#[from] git::ext::Error),
#[error("invalid repository identifier {0:?}")]
InvalidId(std::ffi::OsString),
#[error("inventory: {0}")]
Inventory(io::Error),
#[error("i/o: {0}")]
Io(#[from] io::Error),
}
@ -405,9 +402,6 @@ impl<V> Deref for Remote<V> {
/// Read-only operations on a storage instance.
pub trait ReadStorage {
type Repository: ReadRepository;
type InventoryRef<'a>: Deref<Target = Inventory>
where
Self: 'a;
/// Get user info for this storage.
fn info(&self) -> &UserInfo;
@ -417,16 +411,8 @@ pub trait ReadStorage {
fn path_of(&self, rid: &RepoId) -> PathBuf;
/// Check whether storage contains a repository.
fn contains(&self, rid: &RepoId) -> Result<bool, RepositoryError>;
/// Get the inventory of repositories hosted under this storage.
/// This function should typically only return public repositories.
fn inventory(&self) -> Result<Inventory, Error>;
/// Return a reference to the inventory. Note that this function may hold a
/// lock to the inventory.
fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a>;
/// Return all repositories (public and private).
fn repositories(&self) -> Result<Vec<RepositoryInfo<Verified>>, Error>;
/// Insert this repository into the inventory.
fn insert(&self, rid: RepoId);
/// Open or create a read-only repository.
fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError>;
/// Get a repository's identity if it exists.
@ -667,7 +653,6 @@ where
S: ReadStorage + 'static,
{
type Repository = S::Repository;
type InventoryRef<'a> = S::InventoryRef<'a> where T: 'a;
fn info(&self) -> &UserInfo {
self.deref().info()
@ -681,25 +666,10 @@ where
self.deref().path_of(rid)
}
fn insert(&self, rid: RepoId) {
self.deref().insert(rid)
}
fn contains(&self, rid: &RepoId) -> Result<bool, RepositoryError> {
self.deref().contains(rid)
}
fn inventory(&self) -> Result<Inventory, Error> {
self.deref().inventory()
}
fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a>
where
T: 'a,
{
self.deref().inventory_ref()
}
fn get(&self, rid: RepoId) -> Result<Option<Doc<Verified>>, RepositoryError> {
self.deref().get(rid)
}

View File

@ -5,7 +5,6 @@ pub mod transport;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, MutexGuard};
use std::{fs, io};
use crypto::{Signer, Verified};
@ -20,8 +19,8 @@ use crate::node::SyncedAt;
use crate::storage::refs;
use crate::storage::refs::{Refs, SignedRefs, SignedRefsAt};
use crate::storage::{
Inventory, ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo,
SetHead, SignRepository, WriteRepository, WriteStorage,
ReadRepository, ReadStorage, Remote, Remotes, RepositoryError, RepositoryInfo, SetHead,
SignRepository, WriteRepository, WriteStorage,
};
use crate::{git, node};
@ -73,34 +72,14 @@ impl<'a> TryFrom<git2::Reference<'a>> for Ref {
}
}
/// A reference to a locked inventory.
pub struct InventoryLock<'a> {
guard: MutexGuard<'a, Option<BTreeSet<RepoId>>>,
}
impl<'a> Deref for InventoryLock<'a> {
type Target = BTreeSet<RepoId>;
#[track_caller]
fn deref(&self) -> &Self::Target {
match *self.guard {
Some(ref cache) => cache,
None => panic!("InventoryLock::deref: inventory cache was not initialized"),
}
}
}
#[derive(Debug, Clone)]
pub struct Storage {
path: PathBuf,
info: UserInfo,
/// Inventory cache. Set to `None` until the cache is populated.
inventory: Arc<Mutex<Option<BTreeSet<RepoId>>>>,
}
impl ReadStorage for Storage {
type Repository = Repository;
type InventoryRef<'a> = InventoryLock<'a>;
fn info(&self) -> &UserInfo {
&self.info
@ -122,44 +101,6 @@ impl ReadStorage for Storage {
Ok(false)
}
fn inventory_ref<'a, 's: 'a>(&'s self) -> InventoryLock<'a> {
let guard = self
.inventory
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
InventoryLock { guard }
}
fn inventory(&self) -> Result<Inventory, Error> {
let mut cache = self
.inventory
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
match *cache {
Some(ref cache) => Ok(cache.clone()),
None => {
let repos: BTreeSet<_> = self.public_repositories()?.collect();
*cache = Some(repos.clone());
Ok(repos)
}
}
}
fn insert(&self, rid: RepoId) {
let mut repos = self
.inventory
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if let Some(ref mut repos) = *repos {
repos.insert(rid);
} else {
*repos = Some(BTreeSet::from_iter([rid]));
}
}
fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError> {
Repository::open(paths::repository(self, &rid), rid)
}
@ -265,11 +206,7 @@ impl Storage {
Err(err) => return Err(Error::Io(err)),
Ok(()) => {}
}
Ok(Self {
path,
info,
inventory: Default::default(),
})
Ok(Self { path, info })
}
/// Create a [`Repository`] in a temporary directory.
@ -336,14 +273,6 @@ impl Storage {
}
Ok(())
}
fn public_repositories(&self) -> Result<impl Iterator<Item = RepoId>, Error> {
let repos = self.repositories()?;
Ok(repos
.into_iter()
.filter(|r| r.doc.visibility.is_public())
.map(|r| r.rid))
}
}
/// Git implementation of [`WriteRepository`] using the `git2` crate.

View File

@ -1,4 +1,4 @@
use std::collections::{BTreeSet, HashMap};
use std::collections::HashMap;
use std::convert::Infallible;
use std::io;
use std::path::{Path, PathBuf};
@ -51,26 +51,20 @@ impl MockStorage {
.expect("MockStorage::repo_mut: repository does not exist")
}
pub fn map(mut self, f: impl Fn(&mut Doc<Verified>)) -> Self {
for repo in self.repos.values_mut() {
f(&mut repo.doc.doc);
}
self
}
pub fn empty() -> Self {
Self::new(Vec::new())
}
}
pub struct InventoryLock {
inner: Inventory,
}
impl std::ops::Deref for InventoryLock {
type Target = BTreeSet<RepoId>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl ReadStorage for MockStorage {
type Repository = MockRepository;
type InventoryRef<'a> = InventoryLock;
fn info(&self) -> &git::UserInfo {
&self.info
@ -88,18 +82,6 @@ impl ReadStorage for MockStorage {
Ok(self.repos.contains_key(rid))
}
fn inventory(&self) -> Result<Inventory, Error> {
Ok(self.repos.keys().cloned().collect::<BTreeSet<_>>())
}
fn inventory_ref<'a, 's: 'a>(&'s self) -> Self::InventoryRef<'a> {
InventoryLock {
inner: self.inventory().unwrap(),
}
}
fn insert(&self, _rid: RepoId) {}
fn repository(&self, rid: RepoId) -> Result<Self::Repository, RepositoryError> {
self.repos
.get(&rid)