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

Proposal: add aliases to Alloy syntax #154

Open
tpaschalis opened this issue Feb 19, 2023 · 9 comments
Open

Proposal: add aliases to Alloy syntax #154

tpaschalis opened this issue Feb 19, 2023 · 9 comments
Assignees
Labels
enhancement New feature or request needs-attention proposal A proposal for new functionality. type/syntax Alloy configuration syntax issues

Comments

@tpaschalis
Copy link
Member

tpaschalis commented Feb 19, 2023

The Alloy syntax may benefit from being able to define references to values so that they can be reused and shared across different parts of a configuration file.

These aliases can be helpful to reducing deduplication when repeating the same values or expressions when writing complex configuration and give a degree of certainty that all references are updated in one go.

At the same time, if overused, they can make a configuration file hard to read by hiding the actual values under one or more layers of indirection. For this reason, aliases should only be used in moderation, in situations where a single value or result is used in many places, or that should only be changed from a central place to help maintain a readable configuration file.

Proposal

I propose that we change the Alloy syntax to allow a new top-level statement that aliases, or assigns a name to an expression, so that it can be reused multiple times throughout a configuration file.
An alias should be nothing more than a shorthand to the matching expression, and would use pass-by-value semantics.

Defining aliases

Defining aliases can make use of the let keyword plus the alias name on the LHS, while the RHS must be any valid syntax expression.

let ALIAS = EXPRESSION

Such definitions can only appear as top-level syntax statements.
Each line can only define a single alias.
Aliases must be valid syntax identifiers.
Alias definitions cannot be recursive.
Aliases cannot conflict with any component or top-level block names.
The expression concept in Alloy syntax should now be changed to include references to aliases, similar to component exports.

Using aliases

Aliases can only be used in the context of a syntax expression.

My initial proposal is that we wouldn’t introduce any new symbols or a special way for referring to aliased values (for example, avoid having $name, locals.name or vals.name) but that they should be used as an IdentifierExpr, much like components exports do.

Usage

Here’s how a configuration file that makes use of aliases could look like.
The first alias is a static value that can be reused throughout the configuration file. The second and third aliases are the results of expressions that make use of the Alloy syntax’s standard library and component exports.

let lvl = “debug”
let rw  = [prometheus.remote_write.staging.receiver]
let tgt = json_decode(remote.http.targets.content)

logging {
  log_level = lvl
}

remote.http "targets" {
  url = env("MY_TARGETS_URL")
}

prometheus.remote_write "staging" {
  endpoint {
    url = env(“RW_URL”)
  }
}

prometheus.scrape "app1" {
  targets    = tgt
  forward_to = rw
}

Implementation

Aliases can be stored along component arguments and exports in the controller’s valueCache under a global NodeID.
We can either use that NodeID to detect dependencies to one or more components similarly to what we do for ConfigNode and update the result of expressions, or simply evaluate the aliased expressions after the entire graph is updated.

Limitations - Concerns

One limitation of this proposed solution is that since aliases are the results of an expression, they cannot store blocks. Two features that could benefit from allowing aliases to represent blocks would be to reuse the same rules in multiple relabeling components, or to conditionally apply processing stages in loki.process once #157 is implemented.

Another limitation is that the pass-by-value semantics proposed here may include memory usage in cases where large values are passed around in multiple aliases.

Feature Naming

I’m partial to the name alias for this feature, since it best captures the idea of it being just another name for referring to an expression. Considering some alternatives:
Variables usually point to something that can be updated, rather than a shorthand for an already-defined expression.
Constants on the other hand point to something immutable, that is not the case with syntax expressions.
Stored values strikes a balance as a term, but is more verbose than I’d like.

Alternatives

Of course, one idea would be to "do nothing" and reason that it is easier for users to always write expressions in full.
Having some experience in writing configurations in these past months, I feel that this feature would be quite useful if used with care.

Another idea would be to avoid introducing aliases on a language level but have an aliases component or configuration block instead, where all shorthands would be grouped together. Personally, I think this somewhat takes away from the feature's power by making it harder to quickly define locally-used shorthands, as well as has the potential of wrongly propagating an unhealthy status (eg. mistyping an expression should not render the entire set or aliases as 'unhealthy'.)

@tpaschalis tpaschalis self-assigned this Feb 19, 2023
@rfratto
Copy link
Member

rfratto commented Feb 19, 2023

I'm in favor of this. This would also be very useful for #156.

Such definitions can only appear as top-level syntax statements.

I think this is a reasonable restriction for now, but I imagine in the future it'd be useful to have component-scoped aliases. It would help you reuse expressions without having to worry about colliding with the name of an alias used outside of the components.

@mattdurham
Copy link
Collaborator

I am hesitant to add a new language-level concept at the top level. Would you have any thoughts on using a component to begin with and seeing how that goes?

alias {
    lvl = "debug"
    rw  = [prometheus.remote_write.staging.receiver]
    tgt = json_decode(remote.http.targets.content)
}

We would organically get to show the values in the UI without making changes, and it's not quite as terse as native aliases but also not overly verbose. Also requires the least amount of change.

@tpaschalis
Copy link
Member Author

tpaschalis commented Feb 20, 2023

I think this is a reasonable restriction for now, but I imagine in the future it'd be useful to have component-scoped aliases

@rfratto I agree it might be useful. The only thing I'm concerned about would be shadowing, but it's not something unique to Alloy syntax.

I am hesitant to add a new language-level concept at the top level. Would you have any thoughts on using a component to begin with and seeing how that goes?

@mattdurham I was also on the fence about this. My main concerns were two
a) Mistyping an expression will result in invalid configuration for the component. This means that it will be regarded as unhealthy, and this will propagate throughout all components using any alias which would be surprising to users.
b) Having standalone statements allows them to be local to where they'll be used, as well as allow them to be more narrowly-scoped elsewhere in the future. The component or config block approach always ties them to the global scope.

Do you think these are important-enough to make us do this at a language level?

@rfratto
Copy link
Member

rfratto commented Feb 20, 2023

Aliases cannot conflict with any component or top-level block names.

Specifically, an alias can't conflict with the namespace of a component (You can't have an alias named prometheus when you have a prometheus.remote_write component defined).

Should aliases be prevented from colliding with any registered namespace? For example, can you name an alias prometheus if you don't have any prometheus.* components in your file?

@rfratto rfratto added the enhancement New feature or request label Feb 28, 2023
@captncraig
Copy link
Contributor

I am for this. I think it could help a number of things that would get repeated in complicated fan-out scenarios.

How would they interact with modules? I am assuming you could pass them into modules (or at least the value they evaluate to), but what happens if a module defines its own aliases internally? Presumably they are namespaced so there is no possibility of corrupting the root aliases.

@oferziss-armis
Copy link

alias component with an address can be even better so if you have multiple block configured across configuration their 'fqdn' will never conflict

alias "my_aliases" {
    level = "debug"
}

alias "not_my_aliases" {
    level = "error"
}

logging {
  log_level = alias.my_aliases.level
}

would make it easier to reduce the chance of conflicts and you will get the validation for free as components already cannot reuse the same address...

@rfratto
Copy link
Member

rfratto commented Aug 16, 2023

Such definitions can only appear as top-level Alloy statements.

One of my concerns is how we document this in a clear way. The way this works is that we introduce a syntax-level concept (aliases), but Flow has restrictions on where aliases may appear.

If we can't find a clear way to document this, maybe it's worth finding a way to support aliases inside of blocks (i.e., components) to make it less confusing for users.

@WoodyWoodsta
Copy link

I know the subject here is "expressions", but I'm wondering if it would make sense to alias blocks themselves as well? For example, I have a number of prometheus.relabel blocks which all use the same rule. At the moment, I have to redefine the rule in each prometheus.relabel block, which is a shame.

@tpaschalis tpaschalis transferred this issue from grafana/agent Oct 12, 2023
@rfratto rfratto transferred this issue from grafana/river Apr 9, 2024
@rfratto rfratto added the type/syntax Alloy configuration syntax issues label Apr 9, 2024
@rfratto rfratto changed the title Proposal: add aliases to River Proposal: add aliases to Alloy syntax Apr 9, 2024
Copy link
Contributor

This issue has not had any activity in the past 30 days, so the needs-attention label has been added to it.
If the opened issue is a bug, check to see if a newer release fixed your issue. If it is no longer relevant, please feel free to close this issue.
The needs-attention label signals to maintainers that something has fallen through the cracks. No action is needed by you; your issue will be kept open and you do not have to respond to this comment. The label will be removed the next time this job runs if there is new activity.
Thank you for your contributions!

@rfratto rfratto added the proposal A proposal for new functionality. label Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs-attention proposal A proposal for new functionality. type/syntax Alloy configuration syntax issues
Projects
Status: Incoming
Development

No branches or pull requests

6 participants