github.com/dolthub/go-mysql-server@v0.18.0/eventscheduler/enabled_event.go (about)

     1  // Copyright 2023 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package eventscheduler
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  )
    26  
    27  // enabledEvent is used for storing a list of events that are enabled in EventScheduler.
    28  type enabledEvent struct {
    29  	edb             sql.EventDatabase
    30  	event           sql.EventDefinition
    31  	nextExecutionAt time.Time
    32  	username        string
    33  	address         string
    34  }
    35  
    36  var _ fmt.Stringer = (*enabledEvent)(nil)
    37  
    38  // newEnabledEvent returns new enabledEvent object and whether it is created successfully. An event
    39  // with ENABLE status might NOT be created if the event SCHEDULE is ended/expired. If the event is expired,
    40  // then this function either updates its status in the database or drops it from the database.
    41  func newEnabledEvent(ctx *sql.Context, edb sql.EventDatabase, event sql.EventDefinition, curTime time.Time) (*enabledEvent, bool, error) {
    42  	if event.Status == sql.EventStatus_Enable.String() {
    43  		nextExecution, eventEnded, err := event.GetNextExecutionTime(curTime)
    44  		if err != nil {
    45  			return nil, false, err
    46  		} else if !eventEnded {
    47  			username, address, err := getUsernameAndAddressFromDefiner(event.Definer)
    48  			if err != nil {
    49  				return nil, false, err
    50  			}
    51  			return &enabledEvent{
    52  				edb:             edb,
    53  				event:           event,
    54  				nextExecutionAt: nextExecution,
    55  				username:        username,
    56  				address:         address,
    57  			}, true, nil
    58  		} else {
    59  			if event.OnCompletionPreserve {
    60  				event.Status = sql.EventStatus_Disable.String()
    61  				_, err = edb.UpdateEvent(ctx, event.Name, event)
    62  				if err != nil {
    63  					return nil, false, err
    64  				}
    65  			} else {
    66  				err = edb.DropEvent(ctx, event.Name)
    67  				if err != nil {
    68  					return nil, false, err
    69  				}
    70  			}
    71  		}
    72  	}
    73  	return nil, false, nil
    74  }
    75  
    76  // getUsernameAndAddressFromDefiner returns username and address parsed from given definer value of an EventDefinition.
    77  func getUsernameAndAddressFromDefiner(definer string) (string, string, error) {
    78  	// make sure definer has username and address information here
    79  	ua := strings.Split(definer, "@")
    80  	if len(ua) != 2 {
    81  		return "", "", fmt.Errorf("invalid definer for the event")
    82  	}
    83  
    84  	username := strings.TrimSuffix(strings.TrimPrefix(ua[0], "`"), "`")
    85  	username = strings.TrimSuffix(strings.TrimPrefix(username, "'"), "'")
    86  
    87  	address := strings.TrimSuffix(strings.TrimPrefix(ua[1], "`"), "`")
    88  	address = strings.TrimSuffix(strings.TrimPrefix(address, "'"), "'")
    89  
    90  	return username, address, nil
    91  }
    92  
    93  // String implements the fmt.Stringer interface
    94  func (e *enabledEvent) String() string {
    95  	return fmt.Sprintf("next execution at: %v, event database: %s, event definition: %v", e.nextExecutionAt, e.edb.Name(), e.event)
    96  }
    97  
    98  // name returns 'database_name.event_name' used as a key for mapping unique events.
    99  func (e *enabledEvent) name() string {
   100  	return fmt.Sprintf("%s.%s", e.edb.Name(), e.event.Name)
   101  }
   102  
   103  // updateEventAfterExecution updates the event's LastExecuted metadata with given execution time and returns whether
   104  // the event is expired. If the event is not expired, this function updates the given enabledEvent with the next
   105  // execution time. If expired, it updates the event's metadata in the database or drop the event from the database.
   106  func (e *enabledEvent) updateEventAfterExecution(ctx *sql.Context, edb sql.EventDatabase, executionTime time.Time) (bool, error) {
   107  	var nextExecutionAt time.Time
   108  	var ended bool
   109  	var err error
   110  	if e.event.HasExecuteAt {
   111  		// one-time event is ended after one execution
   112  		ended = true
   113  	} else {
   114  		nextExecutionAt, ended, err = e.event.GetNextExecutionTime(time.Now())
   115  		if err != nil {
   116  			return ended, err
   117  		}
   118  	}
   119  
   120  	if ended {
   121  		if e.event.OnCompletionPreserve {
   122  			e.event.Status = sql.EventStatus_Disable.String()
   123  		} else {
   124  			err = edb.DropEvent(ctx, e.event.Name)
   125  			if err != nil {
   126  				return ended, err
   127  			}
   128  			return true, nil
   129  		}
   130  	} else {
   131  		e.nextExecutionAt = nextExecutionAt
   132  	}
   133  
   134  	e.event.LastExecuted = executionTime
   135  	// update the database stored event with LastExecuted and Status metadata update if applicable.
   136  	_, err = edb.UpdateEvent(ctx, e.event.Name, e.event)
   137  	if err != nil {
   138  		return ended, err
   139  	}
   140  
   141  	return ended, nil
   142  }
   143  
   144  // enabledEventsList is a list of enabled events of all databases that the eventExecutor
   145  // uses to execute them at the scheduled time.
   146  type enabledEventsList struct {
   147  	mu         *sync.Mutex
   148  	eventsList []*enabledEvent
   149  }
   150  
   151  // newEnabledEventsList returns new enabledEventsList object with the given
   152  // enabledEvent list and sorts it by the nextExecutionAt time.
   153  func newEnabledEventsList(list []*enabledEvent) *enabledEventsList {
   154  	newList := &enabledEventsList{
   155  		mu:         &sync.Mutex{},
   156  		eventsList: list,
   157  	}
   158  	sort.SliceStable(newList.eventsList, func(i, j int) bool {
   159  		return list[i].nextExecutionAt.Sub(list[j].nextExecutionAt).Seconds() < 1
   160  	})
   161  	return newList
   162  }
   163  
   164  // clear sets the current list to empty list.
   165  func (l *enabledEventsList) clear() {
   166  	l.mu.Lock()
   167  	defer l.mu.Unlock()
   168  	l.eventsList = nil
   169  }
   170  
   171  // len returns the length of the current list.
   172  func (l *enabledEventsList) len() int {
   173  	l.mu.Lock()
   174  	defer l.mu.Unlock()
   175  	return len(l.eventsList)
   176  }
   177  
   178  // getNextExecutionTime returns the execution time of the first enabledEvent in the current list.
   179  func (l *enabledEventsList) getNextExecutionTime() (time.Time, bool) {
   180  	l.mu.Lock()
   181  	defer l.mu.Unlock()
   182  	if len(l.eventsList) == 0 {
   183  		return time.Time{}, false
   184  	}
   185  	return l.eventsList[0].nextExecutionAt, true
   186  }
   187  
   188  // peek returns the first element from the list, without removing it from the list.
   189  func (l *enabledEventsList) peek() *enabledEvent {
   190  	l.mu.Lock()
   191  	defer l.mu.Unlock()
   192  	if len(l.eventsList) == 0 {
   193  		return nil
   194  	}
   195  	return l.eventsList[0]
   196  }
   197  
   198  // pop returns the first element and removes it from the list.
   199  func (l *enabledEventsList) pop() *enabledEvent {
   200  	l.mu.Lock()
   201  	defer l.mu.Unlock()
   202  	if len(l.eventsList) == 0 {
   203  		return nil
   204  	}
   205  	firstInList := l.eventsList[0]
   206  	l.eventsList = l.eventsList[1:]
   207  	return firstInList
   208  }
   209  
   210  // add adds the event to the list and sorts the list.
   211  func (l *enabledEventsList) add(event *enabledEvent) {
   212  	l.mu.Lock()
   213  	defer l.mu.Unlock()
   214  	l.eventsList = append(l.eventsList, event)
   215  	sort.SliceStable(l.eventsList, func(i, j int) bool {
   216  		return l.eventsList[i].nextExecutionAt.Sub(l.eventsList[j].nextExecutionAt).Seconds() < 1
   217  	})
   218  }
   219  
   220  // remove removes the event from the list,
   221  // the list order stays the same.
   222  func (l *enabledEventsList) remove(key string) {
   223  	l.mu.Lock()
   224  	defer l.mu.Unlock()
   225  	for i, e := range l.eventsList {
   226  		if e.name() == key {
   227  			l.eventsList = append(l.eventsList[:i], l.eventsList[i+1:]...)
   228  			return
   229  		}
   230  	}
   231  }
   232  
   233  // String implements the fmt.Stringer interface
   234  func (l *enabledEventsList) String() string {
   235  	return fmt.Sprintf("event list: %v", l.eventsList)
   236  }
   237  
   238  // remove removes all events of the given database from the list,
   239  // the list order stays the same.
   240  func (l *enabledEventsList) removeSchemaEvents(dbName string) {
   241  	l.mu.Lock()
   242  	defer l.mu.Unlock()
   243  	for i, e := range l.eventsList {
   244  		if e.edb.Name() == dbName {
   245  			l.eventsList = append(l.eventsList[:i], l.eventsList[i+1:]...)
   246  		}
   247  	}
   248  }
   249  
   250  // runningEventsStatus stores whether the event is currently running and
   251  // needs to be re-added after execution. When currently running event is
   252  // updated or dropped, it should not be re-added to the enabledEventsList
   253  // after execution.
   254  type runningEventsStatus struct {
   255  	mu     *sync.Mutex
   256  	status map[string]bool
   257  	reAdd  map[string]bool
   258  }
   259  
   260  // newRunningEventsStatus returns new empty runningEventsStatus object.
   261  func newRunningEventsStatus() *runningEventsStatus {
   262  	return &runningEventsStatus{
   263  		mu:     &sync.Mutex{},
   264  		status: make(map[string]bool),
   265  		reAdd:  make(map[string]bool),
   266  	}
   267  }
   268  
   269  // clear removes all entries from runningEventsStatus object maps.
   270  func (r *runningEventsStatus) clear() {
   271  	r.mu.Lock()
   272  	defer r.mu.Unlock()
   273  	r.status = make(map[string]bool)
   274  	r.reAdd = make(map[string]bool)
   275  }
   276  
   277  // update updates the runningEventsStatus object maps with given key and values.
   278  func (r *runningEventsStatus) update(key string, status, reAdd bool) {
   279  	r.mu.Lock()
   280  	defer r.mu.Unlock()
   281  	r.status[key] = status
   282  	r.reAdd[key] = reAdd
   283  }
   284  
   285  // remove removes an entry from runningEventsStatus object maps with given key.
   286  func (r *runningEventsStatus) remove(key string) {
   287  	r.mu.Lock()
   288  	defer r.mu.Unlock()
   289  	delete(r.status, key)
   290  	delete(r.reAdd, key)
   291  }
   292  
   293  // getStatus returns the status of the event at given key.
   294  func (r *runningEventsStatus) getStatus(key string) (bool, bool) {
   295  	r.mu.Lock()
   296  	defer r.mu.Unlock()
   297  	b, ok := r.status[key]
   298  	return b, ok
   299  }
   300  
   301  // getReAdd returns whether to re-add the event at given key.
   302  func (r *runningEventsStatus) getReAdd(key string) (bool, bool) {
   303  	r.mu.Lock()
   304  	defer r.mu.Unlock()
   305  	b, ok := r.reAdd[key]
   306  	return b, ok
   307  }
   308  
   309  // cancelEventsForDatabase marks all running events for the specified database
   310  // so that they do not get rescheduled after they finish running.
   311  func (r *runningEventsStatus) cancelEventsForDatabase(dbName string) {
   312  	r.mu.Lock()
   313  	defer r.mu.Unlock()
   314  	// if there are any running events of given database, then set reAdd to false
   315  	for evId := range r.status {
   316  		if strings.HasPrefix(evId, fmt.Sprintf("%s.", dbName)) {
   317  			r.reAdd[evId] = false
   318  		}
   319  	}
   320  }