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

Cron ticker implementation #837

Closed
wants to merge 22 commits into from
Closed

Cron ticker implementation #837

wants to merge 22 commits into from

Conversation

husio
Copy link
Contributor

@husio husio commented Jun 28, 2019

Implement a message cron ticker.

This is an incomplete implementation. I want to show my approach first
to make sure it is correct before writing the implementation.

There are some decistions I had to make that I am not sure they were the
right ones.

  1. When scheduling a task, we schedule a message for later execution.
    When time will come, a phony transaction containing this message is
    executed.
  2. Because I am using a phony transaction, no transaction attributes can
    be enforced.
  3. All messages are executed in a single transaction. This means that a
    single broken message can stop the whole queue from progressing.
  4. All handler responses are ignored as the weave.Ticker interface
    does not return almost anything on success.

This is work necessary for #671

@husio husio requested a review from ethanfrey June 28, 2019 10:11
@husio husio changed the title This is a work in progress for #671 Cron ticker implementation Jun 28, 2019
@iov-bot-danger
Copy link

1 Error
🚫 Please include a CHANGELOG entry.
You can find it at CHANGELOG.md.

Generated by 🚫 Danger

@ethanfrey
Copy link
Contributor

All messages are executed in a single transaction. This means that a single broken message can stop the whole queue from progressing.

We should run each in a separate CacheWrap like we do with Deliver. If one job fails, it should be marked an error and removed from queue, and others can continue to run on future runs.

@ethanfrey
Copy link
Contributor

All handler responses are ignored as the weave.Ticker interface does not return almost anything on success.

Let's update this. As of tendermint 0.14 or whenever I wrote this, BeginBlock returned nothing. It now returns tags: https://github.com/tendermint/tendermint/blob/v0.31.5/abci/types/types.proto#L167-L169

I would then allow DeliverResult response. We can use an error to signal discarding the cachewrap (similar to Savepoint Decorator). And from the positive result, just include the tags in the BeginBlock return value.

Copy link
Contributor

@ethanfrey ethanfrey left a comment

Choose a reason for hiding this comment

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

Nice start and good queue design.

Some comments to help proceed

queue/queue.go Outdated
if ok, err := db.Has(key); err != nil {
return errors.Wrap(err, "cannot check key existance")
} else if ok {
// If the key is already in use, instead of storing a
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Fair tradeoff. We need to ensure this queue cannot be spammed in any case

queue/queue.go Outdated
// it := db.Iterate(since, until)
// ...

// TODO: iterate from 0 a.d. till now and return the first message
Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds like a good approach. Clear TODO comment

queue/queue.go Outdated

func queueKey(t time.Time) []byte {
rawTime := make([]byte, 8)
binary.BigEndian.PutUint64(rawTime, uint64(t.UnixNano()))
Copy link
Contributor

Choose a reason for hiding this comment

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

Nanosecond? We could round off to second.

That limits how many messages can be processed per block to 1/second of block time. Which is actually a nice feature in my opinion.

Either is fine, just comment on the precision

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have added the comment and changed the granularity to second in 3c31063

Copy link
Contributor

Choose a reason for hiding this comment

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

You mentioned needing some reliable id to identify these issues. Does it need to be a primary key with an index based on time? Or can we just add some arbitrary "id" label to the payload (not just Msg)?

Let us think a bit more on this one. You also realize

var msg Msg
msg.Unmarshal(data)

will always fail, as we need a concrete instance... which means we need some oneof casting here. (This is the reason go-amino allows runtime registration on interfaces. It does make coding in go easier, but at the cost of an undocumented and unmaintainable api for the client)

queue/queue.go Outdated
// Put queues the message in the database to be executed at given time.
// Due to the implementation details, message is guaranteed to be executed
// after given time, but not exactly at given time.
func Put(db weave.KVStore, runAt time.Time, msg weave.Msg) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is good.
I like serializing the msg and registering a handler (similar to gov).

Thinking this through, I realized something is missing and I have no clear way forward.... Authentication info (aka context). Is there a way to serialize context (using interface keys, I highly doubt it)?

We need to know what authority the transaction has when executed, and things like TextResolutionMsg must have other context, like the successful proposal that triggered it. You can complete this PR and merge without context, but it is essential to support serialized authorization somehow. I admit that is a large problem, so just getting a working queue merged in is a great step.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I serialize a transaction instead? This way there is no difference between executing a transaction straight away and executing with a delay.

Copy link
Contributor

Choose a reason for hiding this comment

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

The context is not the actual transaction. Transactions encapsulate requests coming from outside the blockchain. All items here originated inside the blockchain, from a trusted module. That means we don't need to evaluate signatures, but do need a way to capture all context.

I reflected some on #822 (comment) and while I had an adverse reaction to storing auth info like that, as it is no longer tied to any modules (insecure), if we do just run trusted modules and pin/audit their codebase, someone entering fake credentials should not be a real issue. And we can just store the contexts in a straight-forward method to remove much complexity.

This example would allow us to just serialize the AuthN []Condition or AuthN []Address items and reuse them on a new transaction. Maybe we should revisit this PR with an expanded request interface that makes it easy to do so?

queue/queue.go Outdated Show resolved Hide resolved
queue/queue.go Outdated Show resolved Hide resolved
@husio husio force-pushed the msg_cron_issue_671 branch from 3c31063 to d942962 Compare July 1, 2019 08:01
cron/cron.go Outdated Show resolved Hide resolved
x/cron/cron.go Outdated

taskCtx := ctx
if prep, ok := task.(ContextPreparer); ok {
taskCtx = prep.Prepare(taskCtx)
Copy link
Contributor Author

@husio husio Jul 2, 2019

Choose a reason for hiding this comment

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

@ethanfrey I am trying to figure out a good way to insert authentication (and other) information into the context. My latest approach is to delegate this to the client.

I have realized that this cannot be delegated to the client completely, because it is heavily dependent on the authentication used by the router. To keep things simple I was planning to use the same router as the rest of the application. This means, that the same sigs and multisig authentication is required.

I wonder if it would not be best to use the same technique for the Task implementation - create a message with middleware fields like multisig and signatures and use extension decorators to work with them. This make the whole functionality very much like a synchronous request handling, which is good.
The problem with this approach is that extension cannot set Task attributes because it does not know about the implementation. 🤔

I feel a bit like I am going in circles with this implementation. I did many improvements compared to the initial version, but now I slowed down and don't quite know how to solve the context issue.

husio added 12 commits July 3, 2019 09:09
Implement a message cron ticker.

This is an incomplete implementation. I want to show my approach first
to make sure it is correct before writing the implementation.

There are some decistions I had to make that I am not sure they were the
right ones.

1. When scheduling a task, we schedule a message for later execution.
   When time will come, a phony transaction containing this message is
   executed.
2. Because I am using a phony transaction, no transaction attributes can
   be enforced.
3. All messages are executed in a single transaction. This means that a
   single broken message can stop the whole queue from progressing.
4. All handler responses are ignored as the `weave.Ticker` interface
   does not return almost anything on success.
@husio husio force-pushed the msg_cron_issue_671 branch from cf25940 to 5583e37 Compare July 3, 2019 07:28
@codecov-io

This comment has been minimized.

@ruseinov
Copy link
Contributor

ruseinov commented Jul 4, 2019

Briefly looked over this - the approach seems good to me.
I will return to this and try running tests locally and viewing code in the IDE to get a better understanding of the details.

@husio
Copy link
Contributor Author

husio commented Jul 4, 2019

I am closing this draft as a I have created a new pull request using a cleaned up commits #850

@husio husio closed this Jul 4, 2019
@husio husio deleted the msg_cron_issue_671 branch July 24, 2019 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants