Skip to content
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

Provide a way to get last file revisions #1096

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Feb 10, 2025

Motivation:

Central Dogma's content API does not return a file revision when the file is modified.
Alternatively, users can use the history API to find the file revision. One downside of the history API is that we couldn't use it to find the last file revisions of multiple files at once, requiring N queries instead.

If the content API can directly return the last file revision, it would eliminate the need to use the history API and execute N queries. Additionally, since the request is simple, caching would be beneficial from the server’s perspective. If multiple servers in the cluster perform the same operation due to a specific event, the cache hit rate is expected to be high.

Modifications:

  • REST API)
    • Add includeLastFileRevision (int) as a query parameter to listFiles and getFiles API. If the value is greater than 1, EntryDto may include the modified file revision.
  • Java Client API)
    • FilesWithRevisionRequest is added to fluent specify includeLastFileRevision in the get files or list files API.
  • Internal)
    • Add blockingFindLastFileRevisions() to search for file revision within a range of revisions. If a file is not found in the revisions, Revision.INIT (1) is filled instead.

Result:

You can now retrieve the last file revisions using the list files API and the get files API.

CentralDogmaRepository repo = client.forRepo("myProj", "myRepo");

Map<String, Entry<?>> listFiles = 
  repo.file(PathPattern.all())
      // Specify the maximum number of revisions to search for the last file revision.
      // If the last file revision is not found from the specified range of revisions, 
      // `Revision.INIT` is returned instead.
      .includeLastFileRevision(100)
      .list()
      .join();

@ikhoon ikhoon added this to the 0.74.0 milestone Feb 11, 2025
@ikhoon ikhoon marked this pull request as ready for review February 11, 2025 05:10
@@ -408,29 +408,68 @@ private static Revision normalizeRevision(AggregatedHttpResponse res) {
@Override
public CompletableFuture<Map<String, EntryType>> listFiles(String projectName, String repositoryName,
Revision revision, PathPattern pathPattern) {
return listFiles0(projectName, repositoryName, revision, pathPattern, -1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question) What do you think of setting the default value as 1 for both client/server side since it seems like 1 represents "don't search the history"?

Personally, I think it makes more sense that:

  1. 0 represents "check backwards history for 0 revisions"
  2. validate on includeLastFileRevision<0

The current implementation seems fine though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 represents "check backwards history for 0 revisions"

If so, should 0 and 1 return the same result?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I think it makes more sense that:
0 represents "check backwards history for 0 revisions"
validate on includeLastFileRevision<0

I meant it feels more intuitive to me that the parameter represents the number of revisions to go backwards. (i.e. Revision#backward(int)).

I think the current implementation is fine as long as 1) the default value is well defined with validation 2) and the javadocs are clear. It might be helpful if the parameter name better represents the meaning of "check x number of revision including the head revision" as well.

super(repo);
this.revision = requireNonNull(revision, "revision");
this.query = requireNonNull(query, "query");
this.includeLastFileRevision = includeLastFileRevision <= 1 ? -1 : includeLastFileRevision;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not need to check equality based on includeLastFileRevision as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's my mistake.

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. 👍 Left some suggestions. 😉


return client.execute(headers(HttpMethod.GET, path.toString()))
.aggregate()
.thenApply(res -> getFiles(normRev, res));
.thenApply(res -> getFiles(res));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.thenApply(res -> getFiles(res));
.thenApply(ArmeriaCentralDogma::getFiles);

*/
public Entry<T> withRevision(Revision revision) {
requireNonNull(revision, "revision");
return new Entry<>(revision, path, type, content, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably cache the allowNullContent and reuse it here for better accuracy?

* @param type the type of the {@link Entry}
* @param <T> the content type. {@link JsonNode} if JSON. {@link String} if text.
*/
public static <T> Entry<T> ofNullable(Revision revision, String path, EntryType type, @Nullable T content) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of removing the content from the parameters if you want to make a nullable content entry?
Otherwise, I think we can use the above method which takes a @Nullable T content.

.filter(entry -> entry.type().type() != Void.class)
// Remove the heading `/`
.map(entry -> entry.path().substring(1))
.collect(Collectors.toList());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Immutable?

final Map<String, Revision> lastRevisionMap = new HashMap<>();

// At this point, we are sure: from.major >= to.major and read lock is acquired.
try (ObjectReader reader = jGitRepository.newObjectReader();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of using the caching object reader that is introduced in #1097? 😉

if (revCommit.getParentCount() == 0) {
break; // Reached the root commit
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably we can add:

assert revCommit.getParentCount() == 1;

Comment on lines +613 to +615
treeWalk.reset();
treeWalk.addTree(revCommit.getTree());
treeWalk.addTree(revCommit.getParent(0).getTree());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do:

Suggested change
treeWalk.reset();
treeWalk.addTree(revCommit.getTree());
treeWalk.addTree(revCommit.getParent(0).getTree());
treeWalk.reset(revCommit.getTree().getId(), revCommit.getParent(0).getTree());

Comment on lines +166 to +167
*
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
*
*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants